├── .codeflow.yml
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.yml
│ ├── config.yml
│ └── feature_request.yml
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── codeql-analysis.yml
│ ├── main.yml
│ ├── notification.yml
│ ├── release-notes.yml
│ ├── tagging.yml
│ └── versioning.yml
├── .gitignore
├── .nvmrc
├── CODEOWNERS
├── LICENSE
├── README.md
├── examples
├── README.md
├── rainbowkit-demo
│ ├── .env
│ ├── .gitignore
│ ├── README.md
│ ├── config-overrides.js
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── cbw.png
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── manifest.json
│ │ ├── mm.png
│ │ └── wc.png
│ └── src
│ │ ├── App.js
│ │ ├── index.js
│ │ ├── styles.css
│ │ └── utils.js
├── verify-webapp
│ ├── README.md
│ ├── a-profile-app
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── src
│ │ │ ├── App.css
│ │ │ ├── App.tsx
│ │ │ ├── favicon.ico
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tailwind.config.js
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── a-wallet
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── main.ts
│ │ │ ├── style.css
│ │ │ ├── util.ts
│ │ │ └── vite-env.d.ts
│ │ └── tsconfig.json
│ ├── demo.gif
│ └── package.json
├── wagmi-demo
│ ├── .env
│ ├── .gitignore
│ ├── README.md
│ ├── config-overrides.js
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── cbw.png
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── manifest.json
│ │ ├── mm.png
│ │ └── wc.png
│ └── src
│ │ ├── App.js
│ │ ├── Modal.js
│ │ ├── connectors.js
│ │ ├── index.js
│ │ ├── styles.css
│ │ └── utils.js
├── web3-onboard-demo
│ ├── .env
│ ├── .gitignore
│ ├── README.md
│ ├── config-overrides.js
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── cbw.png
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── manifest.json
│ │ ├── mm.png
│ │ └── wc.png
│ └── src
│ │ ├── App.js
│ │ ├── index.js
│ │ ├── styles.css
│ │ └── utils.js
├── web3-react-demo
│ ├── .env
│ ├── .gitignore
│ ├── README.md
│ ├── config-overrides.js
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── cbw.png
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── manifest.json
│ │ ├── mm.png
│ │ └── wc.png
│ └── src
│ │ ├── App.js
│ │ ├── Modal.js
│ │ ├── connectors.js
│ │ ├── index.js
│ │ ├── networks.js
│ │ ├── styles.css
│ │ └── utils.js
└── web3modal-demo
│ ├── .env
│ ├── .gitignore
│ ├── README.md
│ ├── config-overrides.js
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── cbw.png
│ ├── favicon.ico
│ ├── index.html
│ ├── manifest.json
│ ├── mm.png
│ └── wc.png
│ └── src
│ ├── App.js
│ ├── index.js
│ ├── networks.js
│ ├── providerOptions.js
│ ├── styles.css
│ └── utils.js
└── packages
└── wallet-sdk
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .prettierignore
├── .prettierrc.yml
├── LICENSE
├── __tests__
├── addressStorage.test.js
└── encryptDecrypt.test.js
├── assets
└── coinbase_wallet_logo_kit.zip
├── babel.config.js
├── compile-assets.js
├── jest.config.ts
├── jest.setup.ts
├── karma.conf.js
├── package.json
├── scripts
└── release.sh
├── src
├── CoinbaseWalletSDK.test.ts
├── CoinbaseWalletSDK.ts
├── __mocks__
│ ├── provider.ts
│ └── relay.ts
├── assets
│ └── wallet-logo.ts
├── components
│ ├── ConnectContent
│ │ ├── ConnectContent.scss
│ │ ├── ConnectContent.tsx
│ │ └── index.tsx
│ ├── ConnectDialog
│ │ ├── ConnectDialog.scss
│ │ ├── ConnectDialog.test.tsx
│ │ ├── ConnectDialog.tsx
│ │ └── index.tsx
│ ├── LinkFlow
│ │ ├── LinkFlow.test.tsx
│ │ ├── LinkFlow.tsx
│ │ └── index.tsx
│ ├── QRCode.tsx
│ ├── Snackbar
│ │ ├── Snackbar.scss
│ │ ├── Snackbar.test.tsx
│ │ ├── Snackbar.tsx
│ │ ├── SnackbarContainer.test.tsx
│ │ └── index.tsx
│ ├── Spinner
│ │ ├── Spinner.scss
│ │ ├── Spinner.test.tsx
│ │ ├── Spinner.tsx
│ │ └── index.tsx
│ ├── TryExtensionContent
│ │ ├── TryExtensionContent.scss
│ │ ├── TryExtensionContent.tsx
│ │ └── index.tsx
│ ├── icons
│ │ ├── ArrowLeftIcon.tsx
│ │ ├── CloseIcon.tsx
│ │ ├── LaptopIcon.tsx
│ │ ├── QRCodeIcon.tsx
│ │ ├── QRLogoCoinbase.tsx
│ │ ├── QRLogoWallet.ts
│ │ ├── SafeIcon.tsx
│ │ ├── StatusDotIcon.tsx
│ │ ├── coinbase-round.svg
│ │ ├── coinbase-wallet-round.svg
│ │ └── coinbase.svg
│ ├── theme.scss
│ └── types.ts
├── connection
│ ├── ClientMessage.ts
│ ├── DiagnosticLogger.test.ts
│ ├── DiagnosticLogger.ts
│ ├── EventListener.ts
│ ├── RxWebSocket.test.ts
│ ├── RxWebSocket.ts
│ ├── ServerMessage.ts
│ ├── SessionConfig.ts
│ └── WalletSDKConnection.ts
├── fixtures
│ └── provider.ts
├── index.ts
├── lib
│ ├── ScopedLocalStorage.test.ts
│ ├── ScopedLocalStorage.ts
│ ├── cssReset.scss
│ └── cssReset.ts
├── provider
│ ├── CoinbaseWalletProvider.test.ts
│ ├── CoinbaseWalletProvider.ts
│ ├── FilterPolyfill.ts
│ ├── JSONRPC.ts
│ ├── SolanaProvider.test.ts
│ ├── SolanaProvider.ts
│ ├── SubscriptionManager.ts
│ ├── WalletSDKUI.test.ts
│ ├── WalletSDKUI.ts
│ ├── WalletUI.ts
│ ├── WalletUIError.ts
│ └── Web3Provider.ts
├── relay
│ ├── EthereumTransactionParams.ts
│ ├── RelayMessage.ts
│ ├── Session.ts
│ ├── WalletSDKRelay.ts
│ ├── WalletSDKRelayAbstract.ts
│ ├── WalletSDKRelayEventManager.test.ts
│ ├── WalletSDKRelayEventManager.ts
│ ├── Web3Method.ts
│ ├── Web3Request.ts
│ ├── Web3RequestCanceledMessage.ts
│ ├── Web3RequestMessage.ts
│ ├── Web3Response.ts
│ ├── Web3ResponseMessage.ts
│ ├── aes256gcm.test.ts
│ ├── aes256gcm.ts
│ └── solana
│ │ ├── SolanaWeb3Method.ts
│ │ ├── SolanaWeb3Request.ts
│ │ └── SolanaWeb3Response.ts
├── types.ts
├── util.test.ts
├── util.ts
├── vendor-js
│ ├── eth-eip712-util
│ │ ├── LICENSE
│ │ ├── abi.js
│ │ ├── index.d.ts
│ │ ├── index.js
│ │ └── util.js
│ └── qrcode-svg
│ │ ├── LICENSE
│ │ ├── index.d.ts
│ │ └── index.js
└── version.ts
├── tsconfig.build.json
├── tsconfig.json
├── webpack.config.js
└── yarn.lock
/.codeflow.yml:
--------------------------------------------------------------------------------
1 | secure:
2 | required_reviews: 1
3 | upstream_repository: coinbase/coinbase-wallet-sdk
4 | operate:
5 | slack_channels:
6 | - "#wallet-squad-build"
7 | - "#wallet-feedback"
8 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: Bug report
2 | description: Create a report to help us improve
3 | title: "Bug: "
4 | labels: ["type: bug"]
5 |
6 | body:
7 | - type: input
8 | id: description
9 | attributes:
10 | label: Describe the bug
11 | description: A clear and concise description of what the bug is.
12 | placeholder: Tell us what you see!
13 | validations:
14 | required: true
15 |
16 | - type: textarea
17 | id: steps
18 | attributes:
19 | label: Steps
20 | description: Steps to reproduce the behavior.
21 | placeholder: |
22 | 1. Go to '...'
23 | 2. Click on '....'
24 | 3. Scroll down to '....'
25 | 4. See error
26 | validations:
27 | required: true
28 |
29 | - type: textarea
30 | id: expected
31 | attributes:
32 | label: Expected behavior
33 | description: A clear and concise description of what you expected to happen.
34 | validations:
35 | required: true
36 |
37 | - type: input
38 | id: version
39 | attributes:
40 | label: Version
41 | description: Which version of Coinbase Wallet SDK
42 |
43 | - type: textarea
44 | id: additional
45 | attributes:
46 | label: Additional info
47 | description: If applicable, include links to screenshots or error logs
48 |
49 | - type: textarea
50 | id: desktop
51 | attributes:
52 | label: Desktop
53 | description: Please fill in details for bugs reported on desktop
54 | placeholder: |
55 | - OS: [e.g. iOS]
56 | - Browser [e.g. chrome, safari]
57 | - Version [e.g. 22]
58 |
59 | - type: textarea
60 | id: smartphone
61 | attributes:
62 | label: Smartphone
63 | description: Please fill in details for bugs reported on smartphone devices
64 | placeholder: |
65 | - Device: [e.g. iPhone6]
66 | - OS: [e.g. iOS8.1]
67 | - Browser [e.g. stock browser, safari]
68 | - Version [e.g. 22]
69 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Coinbase Wallet Developer Docs
4 | url: https://docs.cloud.coinbase.com/wallet-sdk/docs
5 | about: Coinbase Wallet's developer documentation
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
1 | name: Feature request
2 | description: Suggest an idea for this project
3 | title: "Feature Request: "
4 | labels: ["type: enhancement"]
5 |
6 | body:
7 | - type: markdown
8 | attributes:
9 | value: |
10 | This issue form is for feature requests only!
11 | If you've found a bug, please use [bug_report](/new?template=bug_report.yml)
12 | - type: textarea
13 | id: problem
14 | attributes:
15 | label: Is your feature request related to a problem? Please describe.
16 | description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
17 |
18 | - type: textarea
19 | id: solution
20 | attributes:
21 | label: Describe the solution you'd like
22 | description: A clear and concise description of what you want to happen.
23 | validations:
24 | required: true
25 |
26 | - type: textarea
27 | id: alternatives
28 | attributes:
29 | label: Describe alternatives you've considered
30 | description: Describe any alternative solutions or features you've considered.
31 |
32 | - type: textarea
33 | id: other
34 | attributes:
35 | label: Additional context
36 | description: Add any other context or screenshots about the feature request here.
37 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### _Summary_
2 |
3 |
6 |
7 | ### _How did you test your changes?_
8 |
9 |
12 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [master]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [master]
20 | schedule:
21 | - cron: "45 14 * * 6"
22 |
23 | # paths-ignore:
24 | # - packages/wallet-native-sdk
25 |
26 | jobs:
27 | analyze:
28 | name: Analyze
29 | runs-on: ubuntu-latest
30 | permissions:
31 | actions: read
32 | contents: read
33 | security-events: write
34 |
35 | strategy:
36 | fail-fast: false
37 | matrix:
38 | language: ["javascript"]
39 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
40 | # Learn more about CodeQL language support at https://git.io/codeql-language-support
41 |
42 | steps:
43 | - name: Checkout repository
44 | uses: actions/checkout@v3
45 |
46 | # Initializes the CodeQL tools for scanning.
47 | - name: Initialize CodeQL
48 | uses: github/codeql-action/init@v2
49 | with:
50 | languages: ${{ matrix.language }}
51 | # If you wish to specify custom queries, you can do so here or in a config file.
52 | # By default, queries listed here will override any specified in a config file.
53 | # Prefix the list here with "+" to use these queries and those in the config file.
54 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
55 |
56 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
57 | # If this step fails, then you should remove it and run the build manually (see below)
58 | - name: Autobuild
59 | uses: github/codeql-action/autobuild@v2
60 |
61 | # ℹ️ Command-line programs to run using the OS shell.
62 | # 📚 https://git.io/JvXDl
63 |
64 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
65 | # and modify them (or add more) to build your code if your project
66 | # uses a compiled language
67 |
68 | #- run: |
69 | # make bootstrap
70 | # make release
71 |
72 | - name: Perform CodeQL Analysis
73 | uses: github/codeql-action/analyze@v2
74 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | lint-check:
7 | name: Lint / Type Check
8 | runs-on: ubuntu-latest
9 | defaults:
10 | run:
11 | working-directory: ./packages/wallet-sdk
12 |
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v3
16 |
17 | - name: Checkout node action
18 | uses: actions/setup-node@v3
19 | with:
20 | node-version-file: '.nvmrc'
21 | cache-dependency-path: '**/yarn.lock'
22 |
23 | - name: Install NPM dependencies
24 | run: yarn install
25 |
26 | - name: Check ESLint
27 | run: yarn lint:eslint
28 |
29 | - name: Check prettier formatting
30 | run: yarn lint:prettier
31 |
32 | - name: Runs compile asset script
33 | run: node ./compile-assets.js
34 |
35 | - name: Check Types
36 | run: yarn lint:types
37 |
38 | tests:
39 | name: Tests
40 | runs-on: ubuntu-latest
41 | defaults:
42 | run:
43 | working-directory: ./packages/wallet-sdk
44 |
45 | strategy:
46 | matrix:
47 | node-version: [14.x, 16.x, 18.x]
48 |
49 | steps:
50 | - name: Checkout
51 | uses: actions/checkout@v3
52 |
53 | - name: Use Node.js ${{ matrix.node-version }}
54 | uses: actions/setup-node@v3
55 | with:
56 | node-version: ${{ matrix.node-version }}
57 | cache-dependency-path: '**/yarn.lock'
58 |
59 | - name: Install NPM dependencies
60 | run: yarn install
61 |
62 | - name: Run Unit Tests
63 | run: yarn test:unit
64 |
--------------------------------------------------------------------------------
/.github/workflows/notification.yml:
--------------------------------------------------------------------------------
1 | name: Slack notifications for PRs and Issues
2 |
3 | on:
4 | pull_request:
5 | types: [opened, reopened]
6 | issues:
7 | types: [opened, reopened]
8 |
9 | env:
10 | CHANNEL_WALLET_FEEDBACK: ${{ vars.CHANNEL_WALLET_FEEDBACK }}
11 | CHANNEL_WALLET_SQUAD_BUILD: ${{ vars.CHANNEL_WALLET_SQUAD_BUILD }}
12 | ON_CALL_WALLET: ${{ vars.ON_CALL_WALLET }}
13 | ON_CALL_BUILD_SQUAD: ${{ vars.ON_CALL_BUILD_SQUAD }}
14 |
15 | jobs:
16 | checks:
17 | runs-on: ubuntu-latest
18 | outputs:
19 | skip: ${{ env.skip }}
20 | steps:
21 | - name: Check spam labels
22 | if: ${{ contains(github.event.*.labels.*.name, 'spam') }}
23 | run: |
24 | echo "skip=true" >> $GITHUB_ENV
25 | echo "::error:: Spam label found."
26 | - name: Check write permission
27 | run: |
28 | permission=$(curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/repos/$REPO/collaborators/$USER/permission | jq -r '.permission')
29 | if [ "$permission" != "admin" ] && [ "$permission" != "write" ]; then
30 | echo "::notice:: User $USER has $permission permission."
31 | exit 0
32 | fi
33 | echo "skip=true" >> $GITHUB_ENV
34 | echo "::error:: User $USER has $permission permission. No slack alert required."
35 | env:
36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
37 | REPO: ${{ github.repository }}
38 | USER: ${{ github.actor }}
39 |
40 | parse-event-details:
41 | runs-on: ubuntu-latest
42 | needs: checks
43 | if: ${{ vars.SLACK_ENABLED == 'true' && needs.checks.outputs.skip != 'true' }}
44 | outputs:
45 | event: ${{ env.event }}
46 | title: ${{ env.title }}
47 | url: ${{ env.url }}
48 | author: ${{ env.author }}
49 | steps:
50 | - name: Set pull request details
51 | if: ${{ github.event_name == 'pull_request' }}
52 | env:
53 | title: ${{ github.event.pull_request.title }}
54 | run: |
55 | echo "event=PR ${{ github.event.action }}" >> $GITHUB_ENV
56 | echo "title=${{ env.title }}" >> $GITHUB_ENV
57 | echo "url=${{ github.event.pull_request.html_url }}" >> $GITHUB_ENV
58 | echo "author=${{ github.event.pull_request.user.login }}" >> $GITHUB_ENV
59 |
60 | - name: Set issue details
61 | if: ${{ github.event_name == 'issues' }}
62 | env:
63 | title: ${{ github.event.issue.title }}
64 | run: |
65 | echo "event=Issue ${{ github.event.action }}" >> $GITHUB_ENV
66 | echo "title=${{ env.title }}" >> $GITHUB_ENV
67 | echo "url=${{ github.event.issue.html_url }}" >> $GITHUB_ENV
68 | echo "author=${{ github.event.issue.user.login }}" >> $GITHUB_ENV
69 |
70 | notify:
71 | runs-on: ubuntu-latest
72 | needs: parse-event-details
73 | steps:
74 | - name: Checkout code
75 | uses: actions/checkout@v3
76 |
77 | - name: Set channel and mention
78 | run: |
79 | if ${{ contains(join(github.event.issue.labels.*.name), 'type:') }}; then
80 | echo "channel=${{ env.CHANNEL_WALLET_FEEDBACK }}" >> $GITHUB_ENV
81 | echo "mention=!subteam^${{ env.ON_CALL_WALLET }}" >> $GITHUB_ENV
82 | else
83 | echo "channel=${{ env.CHANNEL_WALLET_SQUAD_BUILD }}" >> $GITHUB_ENV
84 | echo "mention=!subteam^${{ env.ON_CALL_BUILD_SQUAD }}" >> $GITHUB_ENV
85 | fi
86 |
87 | - name: Set text
88 | run: |
89 | text=$(echo "${{ vars.SLACK_TEMPLATE }}")
90 | text=${text//'{{event}}'/${{ needs.parse-event-details.outputs.event }}}
91 | text=${text//'{{title}}'/${{ needs.parse-event-details.outputs.title }}}
92 | text=${text//'{{url}}'/${{ needs.parse-event-details.outputs.url }}}
93 | text=${text//'{{author}}'/${{ needs.parse-event-details.outputs.author }}}
94 | text=${text//'{{mention}}'/${{ env.mention }}}
95 | text=${text//'{{repo}}'/${{ github.repository }}}
96 | text="${text//$'\r\n'/'\n'}"
97 | text="${text//$'\n'/'\n'}"
98 | echo "text=$text" >> $GITHUB_ENV
99 |
100 | - name: Notify Slack
101 | uses: slackapi/slack-github-action@v1.23.0
102 | with:
103 | payload: |
104 | {
105 | "channel": "#${{ env.channel }}",
106 | "text": "${{ env.text }}",
107 | "icon_emoji": ":${{ vars.ICON_EMOJI }}:"
108 | }
109 | env:
110 | SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
111 |
--------------------------------------------------------------------------------
/.github/workflows/release-notes.yml:
--------------------------------------------------------------------------------
1 | name: Release notes
2 |
3 | on:
4 | push:
5 | tags:
6 | - "v*.*.*"
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | name: Create release notes
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v2
15 | - name: Release
16 | uses: softprops/action-gh-release@v1
17 | with:
18 | generate_release_notes: true
19 | draft: true
20 |
--------------------------------------------------------------------------------
/.github/workflows/tagging.yml:
--------------------------------------------------------------------------------
1 | name: Version - Tag
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | autotag:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v2
13 | - uses: butlerlogic/action-autotag@stable
14 | env:
15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
16 | with:
17 | root: "./packages/wallet-sdk"
18 | tag_prefix: "v"
19 |
--------------------------------------------------------------------------------
/.github/workflows/versioning.yml:
--------------------------------------------------------------------------------
1 | name: Version - PR
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | packageVersion:
7 | description: "The version to publish in MAJOR.MINOR.PATCH format"
8 | required: true
9 | default: ""
10 |
11 | jobs:
12 | authorize:
13 | name: Authorize
14 | runs-on: ubuntu-latest
15 | steps:
16 | - name: ${{ github.actor }} permission check to update release version
17 | uses: "lannonbr/repo-permission-check-action@2.0.2"
18 | with:
19 | permission: "write"
20 | env:
21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
22 |
23 | version:
24 | name: Updates package version
25 | runs-on: ubuntu-latest
26 | defaults:
27 | run:
28 | working-directory: ./packages/wallet-sdk
29 | needs: [authorize]
30 | env:
31 | PACKAGE_VERSION: ${{ github.event.inputs.packageVersion }}
32 |
33 | steps:
34 | - name: Checkout
35 | uses: actions/checkout@v3
36 |
37 | - name: Use Node.js
38 | uses: actions/setup-node@v3
39 | with:
40 | node-version-file: '.nvmrc'
41 | cache-dependency-path: '**/yarn.lock'
42 |
43 | - name: Update version
44 | run: yarn version --new-version ${{ env.PACKAGE_VERSION }} --no-git-tag-version
45 |
46 | - name: Update version.ts file
47 | run: yarn prebuild
48 |
49 | - name: Commit and branch updated version
50 | uses: peter-evans/create-pull-request@v4
51 | with:
52 | title: "[Version update] v${{ env.PACKAGE_VERSION }}"
53 | body: "Automated workflow: version update"
54 | branch: release-v${{ env.PACKAGE_VERSION }}
55 | delete-branch: true
56 | commit-message: "v${{ env.PACKAGE_VERSION }}"
57 | labels: version-update
58 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # misc
2 | .DS_Store
3 |
4 | # Native mobile
5 |
6 | ## User settings
7 | xcuserdata/
8 |
9 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
10 | *.xcscmblueprint
11 | *.xccheckout
12 |
13 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
14 | build/
15 | DerivedData/
16 | *.moved-aside
17 | *.pbxuser
18 | !default.pbxuser
19 | *.mode1v3
20 | !default.mode1v3
21 | *.mode2v3
22 | !default.mode2v3
23 | *.perspectivev3
24 | !default.perspectivev3
25 |
26 | ## Obj-C/Swift specific
27 | *.hmap
28 |
29 | ## App packaging
30 | *.ipa
31 | *.dSYM.zip
32 | *.dSYM
33 |
34 | ## Playgrounds
35 | timeline.xctimeline
36 | playground.xcworkspace
37 |
38 | # Swift Package Manager
39 | #
40 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
41 | # Packages/
42 | # Package.pins
43 | # Package.resolved
44 | # *.xcodeproj
45 | #
46 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
47 | # hence it is not needed unless you have added a package configuration file to your project
48 | # .swiftpm
49 |
50 | .build/
51 |
52 | # CocoaPods
53 | #
54 | # We recommend against adding the Pods directory to your .gitignore. However
55 | # you should judge for yourself, the pros and cons are mentioned at:
56 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
57 | #
58 | Pods/
59 | #
60 | # Add this line if you want to avoid checking in source code from the Xcode workspace
61 | *.xcworkspace
62 |
63 | # Carthage
64 | #
65 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
66 | # Carthage/Checkouts
67 |
68 | Carthage/Build/
69 |
70 | # Accio dependency management
71 | Dependencies/
72 | .accio/
73 |
74 | # fastlane
75 | #
76 | # It is recommended to not store the screenshots in the git repo.
77 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
78 | # For more information about the recommended setup visit:
79 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
80 |
81 | fastlane/report.xml
82 | fastlane/Preview.html
83 | fastlane/screenshots/**/*.png
84 | fastlane/test_output
85 |
86 | # Code Injection
87 | #
88 | # After new code Injection tools there's a generated folder /iOSInjectionProject
89 | # https://github.com/johnno1962/injectionforxcode
90 |
91 | iOSInjectionProject/
92 | .DS_Store
93 | xcshareddata
94 |
95 | # Gradle files
96 | .gradle/
97 | build/
98 |
99 | # Local configuration file (sdk path, etc)
100 | local.properties
101 |
102 | # Log/OS Files
103 | *.log
104 |
105 | # Android Studio generated files and folders
106 | captures/
107 | .externalNativeBuild/
108 | .cxx/
109 | *.apk
110 | output.json
111 |
112 | # IntelliJ
113 | *.iml
114 | .idea/
115 | misc.xml
116 | deploymentTargetDropDown.xml
117 | render.experimental.xml
118 |
119 | # Keystore files
120 | *.jks
121 | *.keystore
122 |
123 | # Google Services (e.g. APIs or Firebase)
124 | google-services.json
125 |
126 | # Android Profiling
127 | *.hprof
128 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 14.17
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @coinbase/coinbase-wallet-sdk
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018-2022 Coinbase, Inc.
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | ## Coinbase Wallet Examples
2 |
3 | ### Verify Web App
4 |
5 | [Verify Web App Example](verify-webapp/)
6 |
7 | - Related documentation: [Deep Linking into the Coinbase Wallet Dapp Browser](https://docs.cloud.coinbase.com/wallet-sdk/docs/deep-link-into-dapp-browser)
8 |
9 | ### Integrate with Rainbowkit
10 |
11 | [Rainbowkit example](rainbowkit-demo/)
12 |
13 | - Related documentation: [Integrating with rainbowkit](https://www.rainbowkit.com/docs/introduction)
14 |
15 | ### Integrate with wagmi
16 |
17 | [wagmi example](wagmi-demo/)
18 |
19 | - Related documentation: [Integrating with wagmi](https://docs.cloud.coinbase.com/wallet-sdk/docs/wagmi)
20 |
21 | ### Integrate with web3-react
22 |
23 | [web3-react example](web3-react-demo/)
24 |
25 | - Related documentation: [Integrating with Web3-React](https://docs.cloud.coinbase.com/wallet-sdk/docs/web3-react)
26 |
27 | ### Integrate with web3modal
28 |
29 | [web3modal example](web3modal-demo/)
30 |
31 | - Related documentation: [Integrating with Web3Modal](https://docs.cloud.coinbase.com/wallet-sdk/docs/web3modal)
32 |
33 | ### Integrate with web3-onboard
34 |
35 | [web3-onboard example](web3-onboard-demo/)
36 |
37 | - Related documentation: [Integrating with Web3-Onboard](https://docs.cloud.coinbase.com/wallet-sdk/docs/web3-onboard)
38 |
--------------------------------------------------------------------------------
/examples/rainbowkit-demo/.env:
--------------------------------------------------------------------------------
1 | GENERATE_SOURCEMAP=false
2 | INFURA_UD=
--------------------------------------------------------------------------------
/examples/rainbowkit-demo/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | /node_modules
3 | /.pnp
4 | .pnp.js
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/examples/rainbowkit-demo/README.md:
--------------------------------------------------------------------------------
1 | # Coinbase Wallet: Integrate with wagmi
2 |
3 | This sample application demonstrates how to integrate Coinbase Wallet into a decentralized application using the wagmi library.
4 |
5 | Install packages:
6 |
7 | `npm install`
8 |
9 | Run app:
10 |
11 | `npm start`
12 |
13 | Open app:
14 |
15 | http://localhost:3000/
16 |
--------------------------------------------------------------------------------
/examples/rainbowkit-demo/config-overrides.js:
--------------------------------------------------------------------------------
1 | const webpack = require("webpack");
2 |
3 | module.exports = function override(config, env) {
4 | config.resolve.fallback = {
5 | ...config.resolve.fallback,
6 | util: require.resolve("util/"),
7 | };
8 | config.resolve.extensions = [
9 | ...config.resolve.extensions,
10 | ".ts",
11 | ".tsx",
12 | ".js",
13 | ];
14 | config.plugins = [
15 | ...config.plugins,
16 | new webpack.DefinePlugin({
17 | "process.env.INFURA_ID": JSON.stringify(process.env.INFURA_ID),
18 | }),
19 | ];
20 |
21 | return config;
22 | };
23 |
--------------------------------------------------------------------------------
/examples/rainbowkit-demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wagmi-demo",
3 | "version": "1.0.0",
4 | "description": "",
5 | "private": true,
6 | "keywords": [],
7 | "main": "src/index.js",
8 | "dependencies": {
9 | "@chakra-ui/icons": "^2.0.2",
10 | "@chakra-ui/react": "^2.2.1",
11 | "@coinbase/wallet-sdk": "^3.2.0",
12 | "@rainbow-me/rainbowkit": "^0.8.1",
13 | "ethers": "^5.7.2",
14 | "react": "^18.1.0",
15 | "react-dom": "^18.1.0",
16 | "util": "^0.12.4",
17 | "wagmi": "^0.9.6"
18 | },
19 | "devDependencies": {
20 | "react-app-rewired": "^2.2.1"
21 | },
22 | "overrides": {
23 | "react-scripts": {
24 | "nth-check": "^2.0.1"
25 | }
26 | },
27 | "scripts": {
28 | "start": "react-app-rewired start",
29 | "build": "react-app-rewired build",
30 | "test": "react-app-rewired test",
31 | "eject": "react-app-rewired eject"
32 | },
33 | "eslintConfig": {
34 | "extends": [
35 | "react-app",
36 | "react-app/jest"
37 | ]
38 | },
39 | "browserslist": {
40 | "production": [
41 | ">0.2%",
42 | "not dead",
43 | "not op_mini all"
44 | ],
45 | "development": [
46 | "last 1 chrome version",
47 | "last 1 firefox version",
48 | "last 1 safari version"
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/examples/rainbowkit-demo/public/cbw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/rainbowkit-demo/public/cbw.png
--------------------------------------------------------------------------------
/examples/rainbowkit-demo/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/rainbowkit-demo/public/favicon.ico
--------------------------------------------------------------------------------
/examples/rainbowkit-demo/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
14 |
15 |
16 |
25 | Coinbase Wallet: Rainbowkit Demo
26 |
27 |
28 |
29 |
32 |
33 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/examples/rainbowkit-demo/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "wagmi Demo",
3 | "name": "Coinbase Wallet with wagmi Demo",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "512x512 192x192 64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
--------------------------------------------------------------------------------
/examples/rainbowkit-demo/public/mm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/rainbowkit-demo/public/mm.png
--------------------------------------------------------------------------------
/examples/rainbowkit-demo/public/wc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/rainbowkit-demo/public/wc.png
--------------------------------------------------------------------------------
/examples/rainbowkit-demo/src/index.js:
--------------------------------------------------------------------------------
1 | import { StrictMode } from "react";
2 | import { createRoot } from 'react-dom/client';
3 | import { ChakraProvider } from "@chakra-ui/react";
4 | import {
5 | getDefaultWallets,
6 | RainbowKitProvider,
7 | } from '@rainbow-me/rainbowkit';
8 | import {
9 | WagmiConfig,
10 | createClient,
11 | configureChains
12 | } from "wagmi";
13 | import { arbitrum, avalanche, bsc, fantom, goerli, mainnet, optimism, polygon, sepolia } from 'wagmi/chains';
14 | import { infuraProvider } from 'wagmi/providers/infura'
15 | import { publicProvider } from 'wagmi/providers/public'
16 | import App from "./App";
17 |
18 | // API key for Ethereum node
19 | // Two popular services are Infura (infura.io) and Alchemy (alchemy.com)
20 | const infuraId = process.env.INFURA_ID;
21 |
22 | // Configure chains for connectors to support
23 | const { chains, provider } = configureChains(
24 | [ arbitrum, avalanche, bsc, fantom, goerli, mainnet, optimism, polygon, sepolia ],
25 | [
26 | infuraProvider({ infuraId }),
27 | publicProvider(),
28 | ]
29 | );
30 |
31 | export const { connectors } = getDefaultWallets({
32 | appName: 'RainbowKit Demo',
33 | chains
34 | });
35 |
36 | const client = createClient({
37 | autoConnect: true,
38 | connectors,
39 | provider
40 | });
41 |
42 | const rootElement = document.getElementById("root");
43 | const root = createRoot(rootElement);
44 | root.render(
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | );
55 |
--------------------------------------------------------------------------------
/examples/rainbowkit-demo/src/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Inter;
3 | text-align: center;
4 | }
5 |
--------------------------------------------------------------------------------
/examples/rainbowkit-demo/src/utils.js:
--------------------------------------------------------------------------------
1 | export const truncateAddress = (address) => {
2 | if (!address) return "No Account";
3 | const match = address.match(
4 | /^(0x[a-zA-Z0-9]{2})[a-zA-Z0-9]+([a-zA-Z0-9]{2})$/
5 | );
6 | if (!match) return address;
7 | return `${match[1]}…${match[2]}`;
8 | };
9 |
--------------------------------------------------------------------------------
/examples/verify-webapp/README.md:
--------------------------------------------------------------------------------
1 | # Demo: Verify Webapp
2 |
3 | Demo app to verify user's wallet address from third-party app. For Android & iOS, but using web for demo app.
4 |
5 | Install packages:
6 |
7 | ```yarn build:app```
8 |
9 | ```yarn build:wallet```
10 |
11 | Run apps:
12 |
13 | ```yarn dev```
14 |
15 | Open app:
16 |
17 | http://localhost:3001/
18 |
19 | 
--------------------------------------------------------------------------------
/examples/verify-webapp/a-profile-app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Verify Demo App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/verify-webapp/a-profile-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "a-profile-app",
3 | "description": "A simple profile app that connects to the wallet and shows the user's profile",
4 | "private": true,
5 | "version": "0.1.0",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "react": "^18.0.0",
13 | "react-dom": "^18.0.0"
14 | },
15 | "devDependencies": {
16 | "@types/react": "^18.0.0",
17 | "@types/react-dom": "^18.0.0",
18 | "@vitejs/plugin-react": "^1.3.0",
19 | "autoprefixer": "^10.4.7",
20 | "ethers": "^5.6.8",
21 | "postcss": "^8.4.14",
22 | "tailwindcss": "^3.0.24",
23 | "typescript": "^4.6.3",
24 | "vite": "^2.9.9"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/examples/verify-webapp/a-profile-app/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/examples/verify-webapp/a-profile-app/src/App.css:
--------------------------------------------------------------------------------
1 | .card-container {
2 | width: 768px;
3 | }
4 |
5 | .card {
6 | background-image: linear-gradient(135deg, #81ffef 10%, #f067b4 100%);
7 | }
8 |
9 | .card-profile {
10 | position: absolute;
11 | top: -80px;
12 | left: 124px;
13 | }
14 |
15 | .card-profile-pic {
16 | width: 150px;
17 | height: 150px;
18 | border: 2px solid white;
19 | }
20 |
--------------------------------------------------------------------------------
/examples/verify-webapp/a-profile-app/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/verify-webapp/a-profile-app/src/favicon.ico
--------------------------------------------------------------------------------
/examples/verify-webapp/a-profile-app/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | body {
6 | margin: 0;
7 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
8 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
9 | sans-serif;
10 | -webkit-font-smoothing: antialiased;
11 | -moz-osx-font-smoothing: grayscale;
12 | background-image: linear-gradient(135deg, #09203f 10%, #537895 100%);
13 | height: 100vh;
14 | overflow: hidden;
15 | border-top: 6px solid #f067b4;
16 | }
17 |
18 | code {
19 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
20 | monospace;
21 | }
22 |
--------------------------------------------------------------------------------
/examples/verify-webapp/a-profile-app/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 |
10 | )
11 |
--------------------------------------------------------------------------------
/examples/verify-webapp/a-profile-app/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/examples/verify-webapp/a-profile-app/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ["./src/**/*.{html,js,tsx}"],
3 | theme: {
4 | extend: {},
5 | },
6 | plugins: [],
7 | }
8 |
--------------------------------------------------------------------------------
/examples/verify-webapp/a-profile-app/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": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"],
20 | "references": [{ "path": "./tsconfig.node.json" }]
21 | }
22 |
--------------------------------------------------------------------------------
/examples/verify-webapp/a-profile-app/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "esnext",
5 | "moduleResolution": "node"
6 | },
7 | "include": ["vite.config.ts"]
8 | }
9 |
--------------------------------------------------------------------------------
/examples/verify-webapp/a-profile-app/vite.config.ts:
--------------------------------------------------------------------------------
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 | server: {
8 | port: 3001,
9 | }
10 | })
11 |
--------------------------------------------------------------------------------
/examples/verify-webapp/a-wallet/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/verify-webapp/a-wallet/favicon.ico
--------------------------------------------------------------------------------
/examples/verify-webapp/a-wallet/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Verify Demo App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/examples/verify-webapp/a-wallet/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "a-wallet",
3 | "description": "An interstitial page to connect to the wallet through the dApp",
4 | "private": true,
5 | "version": "0.1.0",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "preview": "vite preview"
10 | },
11 | "devDependencies": {
12 | "ethers": "^5.6.8",
13 | "typescript": "^4.6.3",
14 | "vite": "^2.9.9"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/verify-webapp/a-wallet/src/main.ts:
--------------------------------------------------------------------------------
1 | import { ethers } from "ethers";
2 | import "./style.css";
3 | import { fetchToken } from "./util";
4 |
5 | const app = document.querySelector("#app")!;
6 | app.innerHTML = `Connecting...`;
7 |
8 | const IOS_UNIVERSAL_LINK = "http://localhost:3001"; // Using web schema for demo purposes
9 | const ANDROID_DEEP_LINK = "pineapple://";
10 | export const APP_NAME = "Pineapple Inc.";
11 |
12 | const token = new URLSearchParams(window.location.search).get("token");
13 |
14 | declare global {
15 | interface Window {
16 | ethereum?: any;
17 | web3?: any;
18 | }
19 | }
20 |
21 | function isAndroid() {
22 | const isAndroidUserAgent = /(android)/i.test(navigator.userAgent);
23 | return isAndroidUserAgent;
24 | }
25 |
26 | async function signEthereum() {
27 | console.info("signEthereum");
28 |
29 | try {
30 | if (!token) {
31 | throw new Error("Not a valid user");
32 | }
33 |
34 | const provider = new ethers.providers.Web3Provider(window.ethereum);
35 | const signer = provider.getSigner();
36 | const address = await signer.getAddress();
37 |
38 | const message = await fetchToken(token);
39 | const signature = await signer.signMessage(message);
40 |
41 | app.innerHTML = `Connected! Redirecting back...`;
42 |
43 | // Redirect URL with callback info
44 | if (isAndroid()) {
45 | // Handle if Android
46 | window.location.href = `${ANDROID_DEEP_LINK}/?address=${address}&sign=${signature}&message=${message}`;
47 | } else {
48 | // Handle if iOS
49 | window.location.href = `${IOS_UNIVERSAL_LINK}/?address=${address}&sign=${signature}&message=${message}`;
50 | }
51 | } catch (err) {
52 | // Display error state
53 | console.error(err);
54 |
55 | if (err instanceof Error) {
56 | app.innerHTML = `${err.message}. Redirecting back ...`;
57 | }
58 |
59 | // Go back to app
60 | setTimeout(() => window.history.back(), 800);
61 | }
62 | }
63 |
64 | async function handleEthereum() {
65 | console.info("handleEthereum");
66 | try {
67 | await window.ethereum.enable();
68 | console.info("enabled");
69 |
70 | setTimeout(signEthereum, 500);
71 | } catch (err) {
72 | console.error(err);
73 | }
74 | }
75 |
76 | function initListener() {
77 | console.info("initListener");
78 | window.addEventListener("ethereum#intialized", handleEthereum, {
79 | once: true,
80 | });
81 |
82 | setTimeout(handleEthereum, 5000); // 5 seconds
83 | }
84 |
85 | window.ethereum ? handleEthereum() : initListener();
86 |
--------------------------------------------------------------------------------
/examples/verify-webapp/a-wallet/src/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | height: 100vh;
3 | font-family: Avenir, Helvetica, Arial, sans-serif;
4 | -webkit-font-smoothing: antialiased;
5 | -moz-osx-font-smoothing: grayscale;
6 | background-image: linear-gradient(135deg, #81ffef 10%, #f067b4 100%);
7 | background-size: 400% 400%;
8 | animation: gradient 15s ease infinite;
9 | }
10 |
11 | #app {
12 | text-align: center;
13 | font-size: larger;
14 | color: #2c3e50;
15 | margin-top: 60px;
16 | }
17 |
18 | @keyframes gradient {
19 | 0% {
20 | background-position: 0% 50%;
21 | }
22 | 50% {
23 | background-position: 100% 50%;
24 | }
25 | 100% {
26 | background-position: 0% 50%;
27 | }
28 | }
29 |
30 | .loader {
31 | display: block;
32 | font-size: 3em;
33 | margin: 0 0.5em;
34 | position: relative;
35 | height: 0.5em;
36 | width: 1em;
37 | margin: 25px auto;
38 | }
39 |
40 | .loader:before,
41 | .loader:after {
42 | content: "";
43 | display: block;
44 | height: 0.5em;
45 | position: absolute;
46 | width: 0.5em;
47 | border-radius: 50%;
48 | }
49 |
50 | .loader:before {
51 | animation: load-before 2s ease-in-out infinite, zoom-before 0.66s ease-in-out infinite;
52 | background: #f067b4;
53 | }
54 |
55 | .loader:after {
56 | animation: load-after 2s ease-in-out infinite, zoom-after 0.66s ease-in-out infinite;
57 | background: #81ffef;
58 | }
59 |
60 | @keyframes load-before {
61 | 0% {
62 | left: 0;
63 | transform: scale(1.1);
64 | }
65 |
66 | 50% {
67 | left: 100%;
68 | transform: scale(1);
69 | }
70 |
71 | 100% {
72 | left: 0;
73 | transform: scale(1.1);
74 | }
75 | }
76 |
77 | @keyframes load-after {
78 | 0% {
79 | left: 100%;
80 | transform: scale(1.1);
81 | }
82 |
83 | 50% {
84 | left: 0;
85 | transform: scale(1);
86 | }
87 |
88 | 100% {
89 | left: 100%;
90 | transform: scale(1.1);
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/examples/verify-webapp/a-wallet/src/util.ts:
--------------------------------------------------------------------------------
1 | import { APP_NAME } from "./main";
2 |
3 | export const fetchToken = (token: string): Promise => {
4 | return new Promise((res, rej) => {
5 | if (token === "test_app_token") {
6 | res(`Verify by signing below to authenticate ${APP_NAME}.`);
7 | } else {
8 | rej("Error verifying user");
9 | }
10 | });
11 | };
12 |
--------------------------------------------------------------------------------
/examples/verify-webapp/a-wallet/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/examples/verify-webapp/a-wallet/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "lib": ["ESNext", "DOM"],
7 | "moduleResolution": "Node",
8 | "strict": true,
9 | "sourceMap": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "esModuleInterop": true,
13 | "noEmit": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitReturns": true,
17 | "skipLibCheck": true
18 | },
19 | "include": ["src"]
20 | }
21 |
--------------------------------------------------------------------------------
/examples/verify-webapp/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/verify-webapp/demo.gif
--------------------------------------------------------------------------------
/examples/verify-webapp/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "verify-webapp",
3 | "version": "0.1.0",
4 | "description": "Sample verify web app",
5 | "scripts": {
6 | "dev:app": "cd ./a-profile-app && yarn dev",
7 | "dev:wallet": "cd ./a-wallet && yarn dev",
8 | "build:app": "cd ./a-profile-app && yarn",
9 | "build:wallet": "cd ./a-wallet && yarn",
10 | "dev": "concurrently --kill-others \"yarn dev:app\" \"yarn dev:wallet\""
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/coinbase/coinbase-wallet-sdk.git"
15 | },
16 | "keywords": [
17 | "verification",
18 | "ethers",
19 | "dapp",
20 | "ethereum"
21 | ],
22 | "author": "Coinbase Engineering",
23 | "license": "ISC",
24 | "bugs": {
25 | "url": "https://github.com/coinbase/coinbase-wallet-sdk/issues"
26 | },
27 | "homepage": "https://github.com/coinbase/coinbase-wallet-sdk/examples/verify-webapp",
28 | "devDependencies": {
29 | "concurrently": "^7.2.1"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/examples/wagmi-demo/.env:
--------------------------------------------------------------------------------
1 | GENERATE_SOURCEMAP=false
2 | INFURA_UD=
--------------------------------------------------------------------------------
/examples/wagmi-demo/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | /node_modules
3 | /.pnp
4 | .pnp.js
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/examples/wagmi-demo/README.md:
--------------------------------------------------------------------------------
1 | # Coinbase Wallet: Integrate with wagmi
2 |
3 | This sample application demonstrates how to integrate Coinbase Wallet into a decentralized application using the wagmi library.
4 |
5 | Install packages:
6 |
7 | `npm install`
8 |
9 | Run app:
10 |
11 | `npm start`
12 |
13 | Open app:
14 |
15 | http://localhost:3000/
16 |
--------------------------------------------------------------------------------
/examples/wagmi-demo/config-overrides.js:
--------------------------------------------------------------------------------
1 | const webpack = require("webpack");
2 |
3 | module.exports = function override(config, env) {
4 | config.resolve.fallback = {
5 | ...config.resolve.fallback,
6 | util: require.resolve("util/"),
7 | };
8 | config.resolve.extensions = [
9 | ...config.resolve.extensions,
10 | ".ts",
11 | ".tsx",
12 | ".js",
13 | ];
14 | config.plugins = [
15 | ...config.plugins,
16 | new webpack.DefinePlugin({
17 | "process.env.INFURA_ID": JSON.stringify(process.env.INFURA_ID),
18 | }),
19 | ];
20 |
21 | return config;
22 | };
23 |
--------------------------------------------------------------------------------
/examples/wagmi-demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wagmi-demo",
3 | "version": "1.0.0",
4 | "description": "",
5 | "private": true,
6 | "keywords": [],
7 | "main": "src/index.js",
8 | "dependencies": {
9 | "@chakra-ui/icons": "^2.0.2",
10 | "@chakra-ui/react": "^2.2.1",
11 | "@coinbase/wallet-sdk": "^3.2.0",
12 | "ethers": "^5.6.8",
13 | "react": "^18.1.0",
14 | "react-dom": "^18.1.0",
15 | "util": "^0.12.4",
16 | "wagmi": "^0.4.10"
17 | },
18 | "devDependencies": {
19 | "react-app-rewired": "^2.2.1"
20 | },
21 | "overrides": {
22 | "react-scripts": {
23 | "nth-check": "^2.0.1"
24 | }
25 | },
26 | "scripts": {
27 | "start": "react-app-rewired start",
28 | "build": "react-app-rewired build",
29 | "test": "react-app-rewired test",
30 | "eject": "react-app-rewired eject"
31 | },
32 | "eslintConfig": {
33 | "extends": [
34 | "react-app",
35 | "react-app/jest"
36 | ]
37 | },
38 | "browserslist": {
39 | "production": [
40 | ">0.2%",
41 | "not dead",
42 | "not op_mini all"
43 | ],
44 | "development": [
45 | "last 1 chrome version",
46 | "last 1 firefox version",
47 | "last 1 safari version"
48 | ]
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/examples/wagmi-demo/public/cbw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/wagmi-demo/public/cbw.png
--------------------------------------------------------------------------------
/examples/wagmi-demo/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/wagmi-demo/public/favicon.ico
--------------------------------------------------------------------------------
/examples/wagmi-demo/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
14 |
15 |
16 |
25 | Coinbase Wallet: wagmi Demo
26 |
27 |
28 |
29 |
30 |
31 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/examples/wagmi-demo/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "wagmi Demo",
3 | "name": "Coinbase Wallet with wagmi Demo",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "512x512 192x192 64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/examples/wagmi-demo/public/mm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/wagmi-demo/public/mm.png
--------------------------------------------------------------------------------
/examples/wagmi-demo/public/wc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/wagmi-demo/public/wc.png
--------------------------------------------------------------------------------
/examples/wagmi-demo/src/Modal.js:
--------------------------------------------------------------------------------
1 | import {
2 | Button,
3 | HStack,
4 | Image,
5 | Modal,
6 | ModalOverlay,
7 | ModalContent,
8 | ModalHeader,
9 | ModalBody,
10 | ModalCloseButton,
11 | Text,
12 | VStack,
13 | } from "@chakra-ui/react";
14 | import { useConnect } from "wagmi";
15 |
16 | export default function SelectWalletModal({ isOpen, closeModal }) {
17 | const { connect, connectors } = useConnect();
18 |
19 | return (
20 |
21 |
22 |
23 | Select Wallet
24 |
29 |
30 |
31 |
50 |
69 |
88 |
89 |
90 |
91 |
92 | );
93 | }
94 |
--------------------------------------------------------------------------------
/examples/wagmi-demo/src/connectors.js:
--------------------------------------------------------------------------------
1 | import { configureChains, defaultChains } from "wagmi";
2 |
3 | import { infuraProvider } from "wagmi/providers/infura";
4 | import { publicProvider } from "wagmi/providers/public";
5 |
6 | import { CoinbaseWalletConnector } from "wagmi/connectors/coinbaseWallet";
7 | import { MetaMaskConnector } from "wagmi/connectors/metaMask";
8 | import { WalletConnectConnector } from "wagmi/connectors/walletConnect";
9 |
10 | // API key for Ethereum node
11 | // Two popular services are Infura (infura.io) and Alchemy (alchemy.com)
12 | const infuraId = process.env.INFURA_ID;
13 |
14 | // Configure chains for connectors to support
15 | const { chains } = configureChains(defaultChains, [
16 | infuraProvider({ infuraId }),
17 | publicProvider(),
18 | ]);
19 |
20 | // Set up connectors
21 | export const connectors = [
22 | new CoinbaseWalletConnector({
23 | chains,
24 | options: {
25 | appName: "wagmi demo",
26 | },
27 | }),
28 | new WalletConnectConnector({
29 | chains,
30 | options: {
31 | infuraId,
32 | qrcode: true,
33 | },
34 | }),
35 | new MetaMaskConnector({
36 | chains,
37 | }),
38 | ];
39 |
--------------------------------------------------------------------------------
/examples/wagmi-demo/src/index.js:
--------------------------------------------------------------------------------
1 | import { StrictMode } from "react";
2 | import { createRoot } from "react-dom/client";
3 | import { ChakraProvider } from "@chakra-ui/react";
4 | import { WagmiConfig, createClient } from "wagmi";
5 | import { connectors } from "./connectors";
6 | import App from "./App";
7 |
8 | const rootElement = document.getElementById("root");
9 | const root = createRoot(rootElement);
10 | const client = createClient({
11 | autoConnect: true,
12 | connectors,
13 | });
14 | root.render(
15 |
16 |
17 |
18 |
19 |
20 |
21 | ,
22 | );
23 |
--------------------------------------------------------------------------------
/examples/wagmi-demo/src/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Inter;
3 | text-align: center;
4 | }
5 |
--------------------------------------------------------------------------------
/examples/wagmi-demo/src/utils.js:
--------------------------------------------------------------------------------
1 | export const truncateAddress = address => {
2 | if (!address) return "No Account";
3 | const match = address.match(
4 | /^(0x[a-zA-Z0-9]{2})[a-zA-Z0-9]+([a-zA-Z0-9]{2})$/,
5 | );
6 | if (!match) return address;
7 | return `${match[1]}…${match[2]}`;
8 | };
9 |
--------------------------------------------------------------------------------
/examples/web3-onboard-demo/.env:
--------------------------------------------------------------------------------
1 | GENERATE_SOURCEMAP=false
2 | INFURA_ID=
--------------------------------------------------------------------------------
/examples/web3-onboard-demo/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | /node_modules
3 | /.pnp
4 | .pnp.js
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/examples/web3-onboard-demo/README.md:
--------------------------------------------------------------------------------
1 | # Coinbase Wallet: Integrate with Web3-Onboard
2 |
3 | This sample application demonstrates how to integrate Coinbase Wallet into a decentralized application using the Web3-Onboard library.
4 |
5 | Install packages:
6 |
7 | `npm install`
8 |
9 | Run app:
10 |
11 | `npm start`
12 |
13 | Open app:
14 |
15 | http://localhost:3000/
16 |
--------------------------------------------------------------------------------
/examples/web3-onboard-demo/config-overrides.js:
--------------------------------------------------------------------------------
1 | const webpack = require("webpack");
2 |
3 | module.exports = function override(config, env) {
4 | config.resolve.fallback = {
5 | ...config.resolve.fallback,
6 | assert: require.resolve("assert/"),
7 | stream: require.resolve("stream-browserify"),
8 | util: require.resolve("util/"),
9 | };
10 | config.resolve.extensions = [
11 | ...config.resolve.extensions,
12 | ".ts",
13 | ".tsx",
14 | ".js",
15 | ];
16 | config.plugins = [
17 | ...config.plugins,
18 | new webpack.DefinePlugin({
19 | "process.env.INFURA_ID": JSON.stringify(process.env.INFURA_ID),
20 | }),
21 | ];
22 |
23 | return config;
24 | };
25 |
--------------------------------------------------------------------------------
/examples/web3-onboard-demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "web3-onboard-demo",
3 | "version": "1.0.0",
4 | "description": "",
5 | "private": true,
6 | "keywords": [],
7 | "main": "src/index.js",
8 | "dependencies": {
9 | "@chakra-ui/icons": "^2.0.2",
10 | "@chakra-ui/react": "^2.2.1",
11 | "@coinbase/wallet-sdk": "^3.2.0",
12 | "@walletconnect/web3-provider": "^1.7.8",
13 | "@web3-onboard/coinbase": "^2.0.3",
14 | "@web3-onboard/core": "^2.2.10",
15 | "@web3-onboard/injected-wallets": "^2.0.7",
16 | "@web3-onboard/walletconnect": "^2.0.1",
17 | "assert": "^2.0.0",
18 | "ethers": "^5.6.8",
19 | "react": "^18.1.0",
20 | "react-dom": "^18.1.0",
21 | "stream-browserify": "^3.0.0",
22 | "util": "^0.12.4",
23 | "web3modal": "^1.9.7"
24 | },
25 | "devDependencies": {
26 | "react-app-rewired": "^2.2.1"
27 | },
28 | "overrides": {
29 | "react-scripts": {
30 | "nth-check": "^2.0.1"
31 | }
32 | },
33 | "scripts": {
34 | "start": "react-app-rewired start",
35 | "build": "react-app-rewired build",
36 | "test": "react-app-rewired test",
37 | "eject": "react-app-rewired eject"
38 | },
39 | "eslintConfig": {
40 | "extends": [
41 | "react-app",
42 | "react-app/jest"
43 | ]
44 | },
45 | "browserslist": {
46 | "production": [
47 | ">0.2%",
48 | "not dead",
49 | "not op_mini all"
50 | ],
51 | "development": [
52 | "last 1 chrome version",
53 | "last 1 firefox version",
54 | "last 1 safari version"
55 | ]
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/examples/web3-onboard-demo/public/cbw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/web3-onboard-demo/public/cbw.png
--------------------------------------------------------------------------------
/examples/web3-onboard-demo/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/web3-onboard-demo/public/favicon.ico
--------------------------------------------------------------------------------
/examples/web3-onboard-demo/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
14 |
15 |
16 |
25 | Coinbase Wallet: Web3-Onboard Demo
26 |
27 |
28 |
29 |
30 |
31 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/examples/web3-onboard-demo/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Web3-Onboard Demo",
3 | "name": "Coinbase Wallet with Web3-Onboard Demo",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "512x512 192x192 64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/examples/web3-onboard-demo/public/mm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/web3-onboard-demo/public/mm.png
--------------------------------------------------------------------------------
/examples/web3-onboard-demo/public/wc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/web3-onboard-demo/public/wc.png
--------------------------------------------------------------------------------
/examples/web3-onboard-demo/src/index.js:
--------------------------------------------------------------------------------
1 | import { StrictMode } from "react";
2 | import { createRoot } from "react-dom/client";
3 | import { ChakraProvider } from "@chakra-ui/react";
4 | import App from "./App";
5 |
6 | const rootElement = document.getElementById("root");
7 | const root = createRoot(rootElement);
8 | root.render(
9 |
10 |
11 |
12 |
13 | ,
14 | );
15 |
--------------------------------------------------------------------------------
/examples/web3-onboard-demo/src/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Inter;
3 | text-align: center;
4 | }
5 |
--------------------------------------------------------------------------------
/examples/web3-onboard-demo/src/utils.js:
--------------------------------------------------------------------------------
1 | export const truncateAddress = address => {
2 | if (!address) return "No Account";
3 | const match = address.match(
4 | /^(0x[a-zA-Z0-9]{2})[a-zA-Z0-9]+([a-zA-Z0-9]{2})$/,
5 | );
6 | if (!match) return address;
7 | return `${match[1]}…${match[2]}`;
8 | };
9 |
10 | export const toHex = num => {
11 | const val = Number(num);
12 | return "0x" + val.toString(16);
13 | };
14 |
--------------------------------------------------------------------------------
/examples/web3-react-demo/.env:
--------------------------------------------------------------------------------
1 | GENERATE_SOURCEMAP=false
2 | INFURA_ID=
3 |
--------------------------------------------------------------------------------
/examples/web3-react-demo/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | /node_modules
3 | /.pnp
4 | .pnp.js
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/examples/web3-react-demo/README.md:
--------------------------------------------------------------------------------
1 | # Coinbase Wallet: Integrate with web3-react
2 |
3 | This sample application demonstrates how to integrate Coinbase Wallet into a decentralized application using the web3-react library.
4 |
5 | Install packages:
6 |
7 | `npm install`
8 |
9 | Run app:
10 |
11 | `npm start`
12 |
13 | Open app:
14 |
15 | http://localhost:3000/
16 |
--------------------------------------------------------------------------------
/examples/web3-react-demo/config-overrides.js:
--------------------------------------------------------------------------------
1 | const webpack = require("webpack");
2 |
3 | module.exports = function override(config, env) {
4 | config.resolve.fallback = {
5 | ...config.resolve.fallback,
6 | buffer: require.resolve("buffer/"),
7 | util: require.resolve("util/"),
8 | };
9 | config.resolve.extensions = [
10 | ...config.resolve.extensions,
11 | ".ts",
12 | ".tsx",
13 | ".js",
14 | ];
15 | config.plugins = [
16 | ...config.plugins,
17 | new webpack.ProvidePlugin({
18 | Buffer: ["buffer", "Buffer"],
19 | }),
20 | new webpack.DefinePlugin({
21 | "process.env.INFURA_ID": JSON.stringify(process.env.INFURA_ID),
22 | }),
23 | ];
24 |
25 | return config;
26 | };
27 |
--------------------------------------------------------------------------------
/examples/web3-react-demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "web3-react-demo",
3 | "version": "1.0.0",
4 | "description": "",
5 | "private": true,
6 | "keywords": [],
7 | "main": "src/index.js",
8 | "dependencies": {
9 | "@chakra-ui/icons": "^2.0.2",
10 | "@chakra-ui/react": "^2.2.1",
11 | "@web3-react/core": "^6.1.9",
12 | "@web3-react/injected-connector": "^6.0.7",
13 | "@web3-react/walletconnect-connector": "^6.2.13",
14 | "@web3-react/walletlink-connector": "^6.2.14",
15 | "buffer": "^6.0.3",
16 | "ethers": "^5.6.8",
17 | "react": "^18.1.0",
18 | "react-dom": "^18.1.0",
19 | "util": "^0.12.4",
20 | "web-vitals": "^2.1.4"
21 | },
22 | "devDependencies": {
23 | "react-app-rewired": "^2.2.1"
24 | },
25 | "overrides": {
26 | "react-scripts": {
27 | "nth-check": "^2.0.1"
28 | }
29 | },
30 | "scripts": {
31 | "start": "react-app-rewired start",
32 | "build": "react-app-rewired build",
33 | "test": "react-app-rewired test",
34 | "eject": "react-app-rewired eject"
35 | },
36 | "eslintConfig": {
37 | "extends": [
38 | "react-app",
39 | "react-app/jest"
40 | ]
41 | },
42 | "browserslist": {
43 | "production": [
44 | ">0.2%",
45 | "not dead",
46 | "not op_mini all"
47 | ],
48 | "development": [
49 | "last 1 chrome version",
50 | "last 1 firefox version",
51 | "last 1 safari version"
52 | ]
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/examples/web3-react-demo/public/cbw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/web3-react-demo/public/cbw.png
--------------------------------------------------------------------------------
/examples/web3-react-demo/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/web3-react-demo/public/favicon.ico
--------------------------------------------------------------------------------
/examples/web3-react-demo/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
14 |
15 |
16 |
25 | Coinbase Wallet: web3-react Demo
26 |
27 |
28 |
29 |
30 |
31 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/examples/web3-react-demo/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "web3-react Demo",
3 | "name": "Coinbase Wallet with web3-react Demo",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "512x512 192x192 64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/examples/web3-react-demo/public/mm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/web3-react-demo/public/mm.png
--------------------------------------------------------------------------------
/examples/web3-react-demo/public/wc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/web3-react-demo/public/wc.png
--------------------------------------------------------------------------------
/examples/web3-react-demo/src/Modal.js:
--------------------------------------------------------------------------------
1 | import {
2 | VStack,
3 | HStack,
4 | Modal,
5 | ModalOverlay,
6 | ModalContent,
7 | ModalHeader,
8 | ModalBody,
9 | ModalCloseButton,
10 | Button,
11 | Text,
12 | } from "@chakra-ui/react";
13 | import { Image } from "@chakra-ui/react";
14 | import { useWeb3React } from "@web3-react/core";
15 | import { connectors } from "./connectors";
16 |
17 | export default function SelectWalletModal({ isOpen, closeModal }) {
18 | const { activate } = useWeb3React();
19 |
20 | const setProvider = type => {
21 | window.localStorage.setItem("provider", type);
22 | };
23 |
24 | return (
25 |
26 |
27 |
28 | Select Wallet
29 |
34 |
35 |
36 |
56 |
76 |
96 |
97 |
98 |
99 |
100 | );
101 | }
102 |
--------------------------------------------------------------------------------
/examples/web3-react-demo/src/connectors.js:
--------------------------------------------------------------------------------
1 | import { InjectedConnector } from "@web3-react/injected-connector";
2 | import { WalletConnectConnector } from "@web3-react/walletconnect-connector";
3 | import { WalletLinkConnector } from "@web3-react/walletlink-connector";
4 |
5 | const injected = new InjectedConnector({
6 | supportedChainIds: [1, 5, 11155111],
7 | });
8 |
9 | const walletconnect = new WalletConnectConnector({
10 | rpcUrl: `https://mainnet.infura.io/v3/${process.env.INFURA_ID}`,
11 | bridge: "https://bridge.walletconnect.org",
12 | qrcode: true,
13 | });
14 |
15 | const walletlink = new WalletLinkConnector({
16 | url: `https://mainnet.infura.io/v3/${process.env.INFURA_ID}`,
17 | appName: "web3-react-demo",
18 | });
19 |
20 | export const connectors = {
21 | injected: injected,
22 | walletConnect: walletconnect,
23 | coinbaseWallet: walletlink,
24 | };
25 |
--------------------------------------------------------------------------------
/examples/web3-react-demo/src/index.js:
--------------------------------------------------------------------------------
1 | import { StrictMode } from "react";
2 | import { createRoot } from "react-dom/client";
3 | import { ChakraProvider } from "@chakra-ui/react";
4 | import { Web3ReactProvider } from "@web3-react/core";
5 | import { ethers } from "ethers";
6 |
7 | import App from "./App";
8 |
9 | const getLibrary = provider => {
10 | const library = new ethers.providers.Web3Provider(provider);
11 | library.pollingInterval = 8000; // frequency provider is polling
12 | return library;
13 | };
14 |
15 | const rootElement = document.getElementById("root");
16 | const root = createRoot(rootElement);
17 | root.render(
18 |
19 |
20 |
21 |
22 |
23 |
24 | ,
25 | );
26 |
--------------------------------------------------------------------------------
/examples/web3-react-demo/src/networks.js:
--------------------------------------------------------------------------------
1 | export const networkParams = {
2 | "0x63564c40": {
3 | chainId: "0x63564c40",
4 | rpcUrls: ["https://api.harmony.one"],
5 | chainName: "Harmony Mainnet",
6 | nativeCurrency: { name: "ONE", decimals: 18, symbol: "ONE" },
7 | blockExplorerUrls: ["https://explorer.harmony.one"],
8 | iconUrls: ["https://harmonynews.one/wp-content/uploads/2019/11/slfdjs.png"],
9 | },
10 | "0xa4ec": {
11 | chainId: "0xa4ec",
12 | rpcUrls: ["https://forno.celo.org"],
13 | chainName: "Celo Mainnet",
14 | nativeCurrency: { name: "CELO", decimals: 18, symbol: "CELO" },
15 | blockExplorerUrl: ["https://explorer.celo.org"],
16 | iconUrls: [
17 | "https://celo.org/images/marketplace-icons/icon-celo-CELO-color-f.svg",
18 | ],
19 | },
20 | };
21 |
--------------------------------------------------------------------------------
/examples/web3-react-demo/src/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Inter;
3 | text-align: center;
4 | }
5 |
--------------------------------------------------------------------------------
/examples/web3-react-demo/src/utils.js:
--------------------------------------------------------------------------------
1 | export const truncateAddress = address => {
2 | if (!address) return "No Account";
3 | const match = address.match(
4 | /^(0x[a-zA-Z0-9]{2})[a-zA-Z0-9]+([a-zA-Z0-9]{2})$/,
5 | );
6 | if (!match) return address;
7 | return `${match[1]}…${match[2]}`;
8 | };
9 |
10 | export const toHex = num => {
11 | const val = Number(num);
12 | return "0x" + val.toString(16);
13 | };
14 |
--------------------------------------------------------------------------------
/examples/web3modal-demo/.env:
--------------------------------------------------------------------------------
1 | GENERATE_SOURCEMAP=false
2 | INFURA_ID=
3 |
--------------------------------------------------------------------------------
/examples/web3modal-demo/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | /node_modules
3 | /.pnp
4 | .pnp.js
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/examples/web3modal-demo/README.md:
--------------------------------------------------------------------------------
1 | # Coinbase Wallet: Integrate with Web3Modal
2 |
3 | This sample application demonstrates how to integrate Coinbase Wallet into a decentralized application using the Web3Modal library.
4 |
5 | Install packages:
6 |
7 | `npm install`
8 |
9 | Run app:
10 |
11 | `npm start`
12 |
13 | Open app:
14 |
15 | http://localhost:3000/
16 |
--------------------------------------------------------------------------------
/examples/web3modal-demo/config-overrides.js:
--------------------------------------------------------------------------------
1 | const webpack = require("webpack");
2 |
3 | module.exports = function override(config, env) {
4 | config.resolve.fallback = {
5 | ...config.resolve.fallback,
6 | assert: require.resolve("assert/"),
7 | buffer: require.resolve("buffer/"),
8 | http: require.resolve("stream-http"),
9 | https: require.resolve("https-browserify"),
10 | os: require.resolve("os-browserify/browser"),
11 | stream: require.resolve("stream-browserify"),
12 | url: require.resolve("url/"),
13 | util: require.resolve("util/"),
14 | };
15 | config.resolve.extensions = [
16 | ...config.resolve.extensions,
17 | ".ts",
18 | ".tsx",
19 | ".js",
20 | ];
21 | config.plugins = [
22 | ...config.plugins,
23 | new webpack.ProvidePlugin({
24 | Buffer: ["buffer", "Buffer"],
25 | }),
26 | new webpack.DefinePlugin({
27 | "process.env.INFURA_ID": JSON.stringify(process.env.INFURA_ID),
28 | }),
29 | ];
30 |
31 | return config;
32 | };
33 |
--------------------------------------------------------------------------------
/examples/web3modal-demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "web3modal-demo",
3 | "version": "1.0.0",
4 | "description": "",
5 | "private": true,
6 | "keywords": [],
7 | "main": "src/index.js",
8 | "dependencies": {
9 | "@chakra-ui/icons": "^2.0.2",
10 | "@chakra-ui/react": "^2.2.1",
11 | "@coinbase/wallet-sdk": "^3.2.0",
12 | "@walletconnect/web3-provider": "^1.7.8",
13 | "assert": "^2.0.0",
14 | "buffer": "^6.0.3",
15 | "ethers": "^5.6.8",
16 | "https-browserify": "^1.0.0",
17 | "os-browserify": "^0.3.0",
18 | "react": "^18.1.0",
19 | "react-dom": "^18.1.0",
20 | "stream-browserify": "^3.0.0",
21 | "stream-http": "^3.2.0",
22 | "url": "^0.11.0",
23 | "util": "^0.12.4",
24 | "web3modal": "^1.9.7"
25 | },
26 | "devDependencies": {
27 | "react-app-rewired": "^2.2.1"
28 | },
29 | "overrides": {
30 | "react-scripts": {
31 | "nth-check": "^2.0.1"
32 | }
33 | },
34 | "scripts": {
35 | "start": "react-app-rewired start",
36 | "build": "react-app-rewired build",
37 | "test": "react-app-rewired test",
38 | "eject": "react-app-rewired eject"
39 | },
40 | "eslintConfig": {
41 | "extends": [
42 | "react-app",
43 | "react-app/jest"
44 | ]
45 | },
46 | "browserslist": {
47 | "production": [
48 | ">0.2%",
49 | "not dead",
50 | "not op_mini all"
51 | ],
52 | "development": [
53 | "last 1 chrome version",
54 | "last 1 firefox version",
55 | "last 1 safari version"
56 | ]
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/examples/web3modal-demo/public/cbw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/web3modal-demo/public/cbw.png
--------------------------------------------------------------------------------
/examples/web3modal-demo/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/web3modal-demo/public/favicon.ico
--------------------------------------------------------------------------------
/examples/web3modal-demo/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
14 |
15 |
16 |
25 | Coinbase Wallet: Web3Modal Demo
26 |
27 |
28 |
29 |
30 |
31 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/examples/web3modal-demo/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Web3Modal Demo",
3 | "name": "Coinbase Wallet with Web3Modal Demo",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "512x512 192x192 64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/examples/web3modal-demo/public/mm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/web3modal-demo/public/mm.png
--------------------------------------------------------------------------------
/examples/web3modal-demo/public/wc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/examples/web3modal-demo/public/wc.png
--------------------------------------------------------------------------------
/examples/web3modal-demo/src/index.js:
--------------------------------------------------------------------------------
1 | import { StrictMode } from "react";
2 | import { createRoot } from "react-dom/client";
3 | import { ChakraProvider } from "@chakra-ui/react";
4 | import App from "./App";
5 |
6 | const rootElement = document.getElementById("root");
7 | const root = createRoot(rootElement);
8 | root.render(
9 |
10 |
11 |
12 |
13 | ,
14 | );
15 |
--------------------------------------------------------------------------------
/examples/web3modal-demo/src/networks.js:
--------------------------------------------------------------------------------
1 | export const networkParams = {
2 | "0x63564c40": {
3 | chainId: "0x63564c40",
4 | rpcUrls: ["https://api.harmony.one"],
5 | chainName: "Harmony Mainnet",
6 | nativeCurrency: { name: "ONE", decimals: 18, symbol: "ONE" },
7 | blockExplorerUrls: ["https://explorer.harmony.one"],
8 | iconUrls: ["https://harmonynews.one/wp-content/uploads/2019/11/slfdjs.png"],
9 | },
10 | "0xa4ec": {
11 | chainId: "0xa4ec",
12 | rpcUrls: ["https://forno.celo.org"],
13 | chainName: "Celo Mainnet",
14 | nativeCurrency: { name: "CELO", decimals: 18, symbol: "CELO" },
15 | blockExplorerUrl: ["https://explorer.celo.org"],
16 | iconUrls: [
17 | "https://celo.org/images/marketplace-icons/icon-celo-CELO-color-f.svg",
18 | ],
19 | },
20 | };
21 |
--------------------------------------------------------------------------------
/examples/web3modal-demo/src/providerOptions.js:
--------------------------------------------------------------------------------
1 | import WalletConnect from "@walletconnect/web3-provider";
2 | import CoinbaseWalletSDK from "@coinbase/wallet-sdk";
3 |
4 | export const providerOptions = {
5 | walletlink: {
6 | package: CoinbaseWalletSDK, // Required
7 | options: {
8 | appName: "Web3Modal Demo", // Required
9 | infuraId: process.env.INFURA_ID, // Required unless you provide a JSON RPC url; see `rpc` below
10 | },
11 | },
12 | walletconnect: {
13 | package: WalletConnect, // required
14 | options: {
15 | infuraId: process.env.INFURA_ID, // required
16 | },
17 | },
18 | };
19 |
--------------------------------------------------------------------------------
/examples/web3modal-demo/src/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Inter;
3 | text-align: center;
4 | }
5 |
--------------------------------------------------------------------------------
/examples/web3modal-demo/src/utils.js:
--------------------------------------------------------------------------------
1 | export const truncateAddress = address => {
2 | if (!address) return "No Account";
3 | const match = address.match(
4 | /^(0x[a-zA-Z0-9]{2})[a-zA-Z0-9]+([a-zA-Z0-9]{2})$/,
5 | );
6 | if (!match) return address;
7 | return `${match[1]}…${match[2]}`;
8 | };
9 |
10 | export const toHex = num => {
11 | const val = Number(num);
12 | return "0x" + val.toString(16);
13 | };
14 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/.eslintignore:
--------------------------------------------------------------------------------
1 | # specific files to ignore
2 | *.md
3 | .eslintrc.js
4 | .eslintignore
5 | webpack.*.js
6 |
7 | # folders to ignore
8 | build
9 | chrome
10 | dist
11 | node_modules
12 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | node_modules
3 | /.pnp
4 | .pnp.js
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 | /dist
12 |
13 | # misc
14 | .DS_Store
15 | .env.local
16 | .env.development.local
17 | .env.test.local
18 | .env.production.local
19 |
20 | npm-debug.log*
21 | yarn-debug.log*
22 | yarn-error.log*
23 |
24 | # eslint cache
25 | .eslintcache
26 |
27 | .vscode
28 | .idea
29 |
30 | *-svg.ts
31 | *-css.ts
32 |
33 | examples/**/yarn.lock
34 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/.prettierignore:
--------------------------------------------------------------------------------
1 | package.json
2 | yarn.lock
3 | coverage/**
4 | **/build
5 | **/dist
6 | **/node_modules
7 | **/src/vendor-js
8 | **/*-css.ts
9 | **/*-svg.ts
--------------------------------------------------------------------------------
/packages/wallet-sdk/.prettierrc.yml:
--------------------------------------------------------------------------------
1 | printWidth: 80
2 | tabWidth: 2
3 | useTabs: false
4 | semi: true
5 | singleQuote: false
6 | trailingComma: all
7 | bracketSpacing: true
8 | bracketSameLine: false
9 | arrowParens: avoid
10 | proseWrap: preserve
11 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018-2020 Coinbase, Inc.
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/__tests__/addressStorage.test.js:
--------------------------------------------------------------------------------
1 | const { ensureAddressString } = require("../build/npm/dist/util");
2 | const { CoinbaseWalletSDK } = require("../build/npm/dist");
3 |
4 | describe("address storage tests", () => {
5 | it("ensureAddressString returns lowercase string", () => {
6 | const input = "0xFadAFCE89EA2221fa33005640Acf2C923312F2b9";
7 | const output = ensureAddressString(input);
8 | expect(output).toEqual("0xfadafce89ea2221fa33005640acf2c923312f2b9");
9 | });
10 |
11 | it("cannot mutate addresses via window.ethereum.enable()", done => {
12 | const coinbaseWalletSDK = new CoinbaseWalletSDK({
13 | appName: "My Awesome DApp",
14 | appLogoUrl: "https://example.com/logo.png",
15 | });
16 |
17 | const provider = coinbaseWalletSDK.makeWeb3Provider(
18 | "https://mainnet.infura.io/v3/INFURA_API_KEY",
19 | 1,
20 | );
21 |
22 | provider._addresses = ["0xfadafce89ea2221fa33005640acf2c923312f2b9"];
23 | void provider.enable().then(addresses => {
24 | addresses[0] = "0xFadAFCE89EA2221fa33005640Acf2C923312F2b9";
25 | expect(provider._addresses[0]).toEqual(
26 | "0xfadafce89ea2221fa33005640acf2c923312f2b9",
27 | );
28 | done();
29 | });
30 | });
31 |
32 | it("cannot mutate addresses via window.ethereum.request eth_accounts", done => {
33 | const coinbaseWalletSDK = new CoinbaseWalletSDK({
34 | appName: "My Awesome DApp",
35 | appLogoUrl: "https://example.com/logo.png",
36 | });
37 |
38 | const provider = coinbaseWalletSDK.makeWeb3Provider(
39 | "https://mainnet.infura.io/v3/INFURA_API_KEY",
40 | 1,
41 | );
42 |
43 | provider._addresses = ["0xfadafce89ea2221fa33005640acf2c923312f2b9"];
44 | void provider.request({ method: "eth_accounts" }).then(addresses => {
45 | addresses[0] = "0xFadAFCE89EA2221fa33005640Acf2C923312F2b9";
46 | expect(provider._addresses[0]).toEqual(
47 | "0xfadafce89ea2221fa33005640acf2c923312f2b9",
48 | );
49 | done();
50 | });
51 | });
52 | });
53 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/__tests__/encryptDecrypt.test.js:
--------------------------------------------------------------------------------
1 | const { encrypt, decrypt } = require("../build/npm/dist/relay/aes256gcm");
2 | const { randomBytesHex } = require("../build/npm/dist/util");
3 |
4 | describe("encryption and decryption tests", () => {
5 | it("decrypted output matches original input", done => {
6 | void (async function () {
7 | const secret = randomBytesHex(32);
8 | const input = "coinbasewallet:2.0";
9 | const cipherText = await encrypt(input, secret);
10 |
11 | decrypt(cipherText, secret).subscribe({
12 | next: decryptedText => {
13 | expect(input).toEqual(decryptedText);
14 | done();
15 | },
16 | });
17 | })();
18 | });
19 |
20 | it("decrypt data sample from client", done => {
21 | (function () {
22 | const cipherText =
23 | "06593325a922a928913b5c6ea26f848c4545bcea4e26c4f5ee745316ff22b2780aeccc565730514b2820a94b03f5f89fe7542a35bbdd87a1d52a4352f49482781113db09266c668696778e0a94bc9f866f1e92e7262fd0bb811838284cc64cbc4552b33e9c6fb2582cea4f49471d6d46a16a5c8ac83ee8483ed4dc01f1fde3bfd7a2f173715e0a8d09dd4907483f096a845bff698831ea277c1ca4223d3f6073174cb35119d0a795c1a9cb4f32ee1dcc254d8931";
24 | const secret =
25 | "c356fe708ea7bbf7b1cc9ff9813c32772b6e0d16332da4c031ba9ea88be9b5ed";
26 | const sampleDataResult =
27 | '{"type":"WEB3_RESPONSE","id":"791fe0ec3dc3de49","response":{"method":"requestEthereumAccounts","result":["0xdf0635793e91d4f8e7426dbd9ed08471186f428d"]}}';
28 | decrypt(cipherText, secret).subscribe({
29 | next: value => {
30 | expect(sampleDataResult).toEqual(value);
31 | done();
32 | },
33 | });
34 | })();
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/assets/coinbase_wallet_logo_kit.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cb-jake/coinbase-wallet-sdk/3d52c09ffb2395fd51107ffc1862381d1e4e641c/packages/wallet-sdk/assets/coinbase_wallet_logo_kit.zip
--------------------------------------------------------------------------------
/packages/wallet-sdk/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | ["@babel/preset-env", { targets: { node: "current" } }],
4 | [
5 | "@babel/preset-typescript",
6 | {
7 | jsxPragma: "h",
8 | jsxPragmaFrag: "Fragment",
9 | },
10 | ],
11 | ],
12 | plugins: [
13 | [
14 | "@babel/plugin-transform-react-jsx",
15 | {
16 | pragma: "h",
17 | pragmaFrag: "Fragment",
18 | },
19 | ],
20 | ["@babel/plugin-proposal-decorators", { legacy: true }],
21 | ],
22 | };
23 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/compile-assets.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | const fs = require("fs");
5 | const glob = require("glob");
6 | const sass = require("sass");
7 | const { optimize } = require("svgo");
8 |
9 | const COPYRIGHT = `// Copyright (c) 2018-2022 Coinbase, Inc.
10 | // Licensed under the Apache License, version 2.0`;
11 |
12 | async function main() {
13 | // compile SCSS
14 | const scssFiles = glob.sync(`${__dirname}/src/**/*.scss`);
15 | for (const filePath of scssFiles) {
16 | console.log(`Compiling ${filePath}...`);
17 | const css = sass
18 | .renderSync({ file: filePath, outputStyle: "compressed" })
19 | .css.toString("utf8");
20 | const ts = `${COPYRIGHT}\n\nexport default \`${css}\``;
21 | fs.writeFileSync(filePath.replace(/\.scss$/, "-css.ts"), ts, {
22 | mode: 0o644,
23 | });
24 | }
25 |
26 | // compile SVG
27 | const svgFiles = glob.sync(`${__dirname}/src/**/*.svg`);
28 | for (const filePath of svgFiles) {
29 | console.log(`Compiling ${filePath}...`);
30 | const svg = fs.readFileSync(filePath, { encoding: "utf8" });
31 | const { data } = optimize(svg, {
32 | path: filePath,
33 | datauri: "base64",
34 | // datauri inlining won't happen until min size has been reached per
35 | // https://github.com/svg/svgo/blob/b37d90e12a87312bba87a6c52780884e6e595e23/lib/svgo.js#L57-L68
36 | // so we enable multipass for that to happen
37 | multipass: true,
38 | });
39 | const ts = `${COPYRIGHT}\n\nexport default \`${data}\``;
40 | fs.writeFileSync(filePath.replace(/\.svg$/, "-svg.ts"), ts, {
41 | mode: 0o644,
42 | });
43 | }
44 |
45 | console.log("DONE!");
46 | }
47 |
48 | main();
49 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/jest.config.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * For a detailed explanation regarding each configuration property and type check, visit:
3 | * https://jestjs.io/docs/configuration
4 | */
5 |
6 | export default {
7 | // Automatically clear mock calls, instances and results before every test
8 | clearMocks: true,
9 |
10 | // Indicates whether the coverage information should be collected while executing the test
11 | collectCoverage: true,
12 |
13 | // An array of glob patterns indicating a set of files for which coverage information should be collected
14 | collectCoverageFrom: [
15 | "./src/util.ts",
16 | "./src/CoinbaseWalletSDK.ts",
17 | "./src/connection/RxWebSocket.ts",
18 | "./src/connection/WalletSDKConnection.ts",
19 | "./src/lib/ScopedLocalStorage.ts",
20 | "./src/provider/CoinbaseWalletProvider.ts",
21 | "./src/provider/SolanaProvider.ts",
22 | "./src/provider/FilterPolyfill.ts",
23 | "./src/provider/SubscriptionManager.ts",
24 | "./src/provider/WalletSDKUI.ts",
25 | "./src/relay/aes256gcm.ts",
26 | "./src/relay/Session.ts",
27 | "./src/relay/WalletSDKRelay.ts",
28 | "./src/relay/WalletSDKRelayEventManager.ts",
29 | "./src/components/**/*.tsx",
30 | ],
31 | // The directory where Jest should output its coverage files
32 | coverageDirectory: "coverage",
33 |
34 | // An array of regexp pattern strings used to skip coverage collection
35 | coveragePathIgnorePatterns: ["/node_modules/"],
36 |
37 | // A list of reporter names that Jest uses when writing coverage reports
38 | coverageReporters: ["json", "text", "text-summary", "lcov"],
39 |
40 | // TODO: Increase threshold as additional tests are added
41 | coverageThreshold: {
42 | global: {
43 | branches: 54,
44 | functions: 55,
45 | statements: 63,
46 | },
47 | },
48 |
49 | // An array of file extensions your modules use
50 | moduleFileExtensions: ["js", "jsx", "ts", "tsx", "json", "node"],
51 |
52 | // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
53 | moduleNameMapper: {
54 | "^src/(.*)$": "/src/$1",
55 | },
56 |
57 | // A list of paths to directories that Jest should use to search for files in
58 | roots: ["/src"],
59 |
60 | // A list of paths to modules that run some code to configure or set up the testing framework before each test
61 | setupFilesAfterEnv: ["/jest.setup.ts"],
62 |
63 | // The test environment that will be used for testing
64 | testEnvironment: "jsdom",
65 |
66 | // The glob patterns Jest uses to detect test files
67 | testMatch: ["/src/**/*.test.[tj]s?(x)"],
68 |
69 | // A map from regular expressions to paths to transformers
70 | transform: {
71 | "^.+\\.(js|ts|tsx)$": ["babel-jest"],
72 | },
73 |
74 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
75 | testPathIgnorePatterns: ["/node_modules/", "/build/"],
76 | };
77 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/jest.setup.ts:
--------------------------------------------------------------------------------
1 | import "@testing-library/jest-dom";
2 |
3 | import { Crypto } from "@peculiar/webcrypto";
4 | import { TextDecoder, TextEncoder } from "util";
5 |
6 | global.crypto = new Crypto();
7 |
8 | global.TextEncoder = TextEncoder;
9 |
10 | // @ts-expect-error Use util TextDecoder
11 | global.TextDecoder = TextDecoder;
12 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function (config) {
2 | config.set({
3 | plugins: ["karma-jasmine", "karma-chrome-launcher", "karma-browserify"],
4 |
5 | // base path that will be used to resolve all patterns (eg. files, exclude)
6 | basePath: "",
7 |
8 | frameworks: ["jasmine", "browserify"],
9 |
10 | // list of files / patterns to load in the browser
11 | files: ["__tests__/**/*.js"],
12 |
13 | preprocessors: {
14 | "__tests__/**/*.js": ["browserify"],
15 | },
16 |
17 | browserify: {
18 | debug: true,
19 | },
20 | });
21 | };
22 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/scripts/release.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # COLORS
4 | REDBOLD='\033[1;31m'
5 | RED='\033[0;31m'
6 | PURPLE='\033[0;35m'
7 | TEAL='\033[0;36m'
8 | GREEN='\033[1;32m'
9 |
10 | gitMessage=$(git log --oneline -n 1)
11 | mainBranch="master"
12 | branch=$(git rev-parse --abbrev-ref HEAD)
13 |
14 | if [ $branch == $mainBranch ]; then
15 | echo -e "${PURPLE}Checking all branches are up-to-date..."
16 | echo -e "================================================="
17 | echo -e " git fetch --all"
18 | git fetch --all
19 | echo -e " git pull"
20 | git pull
21 | echo -e "-------------------------------------------------"
22 | echo -e "${TEAL}Build production and publish..."
23 | echo "================================================="
24 | echo -e "rm -rf ./node_modules"
25 | rm -rf ./node_modules
26 | echo -e "yarn install"
27 | yarn install
28 | echo -e "yarn build:prod"
29 | yarn build:prod
30 | echo "================================================="
31 | echo -e " ${GREEN}cd build/npm and run 'npm publish'"
32 | echo "================================================="
33 | else
34 | echo -e "${RED}⚠️ Need to publish from ${mainBranch} branch"
35 | echo -e "${REDBOLD}Checking out ${mainBranch}... "
36 | git checkout master
37 | echo -e "${RED}Run again"
38 | fi
39 |
40 | # TODO: Add Slack notification?
41 | # - Add release notes generation?
42 | # - Add version tag as argument?
43 | # - Add prompt for npm login?
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/__mocks__/provider.ts:
--------------------------------------------------------------------------------
1 | import { ScopedLocalStorage } from "../lib/ScopedLocalStorage";
2 | import {
3 | CoinbaseWalletProvider,
4 | CoinbaseWalletProviderOptions,
5 | } from "../provider/CoinbaseWalletProvider";
6 | import { WalletSDKRelayEventManager } from "../relay/WalletSDKRelayEventManager";
7 |
8 | export const mockSetAppInfo = jest.fn();
9 |
10 | export class MockProviderClass extends CoinbaseWalletProvider {
11 | constructor(opts: Readonly) {
12 | super(opts);
13 | }
14 |
15 | public async close() {
16 | return Promise.resolve();
17 | }
18 |
19 | // @ts-expect-error mock relay
20 | private async initializeRelay() {
21 | return Promise.resolve({
22 | setAppInfo: mockSetAppInfo,
23 | });
24 | }
25 | }
26 |
27 | export const mockExtensionProvider = new MockProviderClass({
28 | chainId: 1,
29 | jsonRpcUrl: "jsonrpc-url",
30 | overrideIsMetaMask: false,
31 | relayEventManager: new WalletSDKRelayEventManager(),
32 | relayProvider: jest.fn(),
33 | storage: new ScopedLocalStorage("-walletlink"),
34 | });
35 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/ConnectContent/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./ConnectContent";
2 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/ConnectDialog/ConnectDialog.scss:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | @import "../theme.scss";
5 |
6 | .-cbwsdk-css-reset {
7 | .-cbwsdk-connect-dialog {
8 | z-index: 2147483647;
9 | position: fixed;
10 | top: 0;
11 | left: 0;
12 | right: 0;
13 | bottom: 0;
14 | display: flex;
15 | align-items: center;
16 | justify-content: center;
17 |
18 | $white: #fff;
19 | $black: #000;
20 |
21 | &-backdrop {
22 | z-index: 2147483647;
23 | position: fixed;
24 | top: 0;
25 | left: 0;
26 | right: 0;
27 | bottom: 0;
28 | transition: opacity 0.25s;
29 |
30 | &.light {
31 | background-color: rgba($black, 0.5);
32 | }
33 |
34 | &.dark {
35 | background-color: rgba(50, 53, 61, 0.4);
36 | }
37 |
38 | &-hidden {
39 | opacity: 0;
40 | }
41 | }
42 |
43 | &-box {
44 | display: flex;
45 | position: relative;
46 | flex-direction: column;
47 | transform: scale(1);
48 | transition: opacity 0.25s, transform 0.25s;
49 |
50 | &-hidden {
51 | opacity: 0;
52 | transform: scale(0.85);
53 | }
54 | }
55 |
56 | &-container {
57 | display: block;
58 |
59 | &-hidden {
60 | display: none;
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/ConnectDialog/ConnectDialog.test.tsx:
--------------------------------------------------------------------------------
1 | import { fireEvent, render, screen, waitFor } from "@testing-library/preact";
2 | import { h } from "preact";
3 |
4 | import { ConnectDialog } from "./ConnectDialog";
5 |
6 | const renderConnectDialog = ({
7 | connectDisabled = false,
8 | isConnected = true,
9 | }) => {
10 | return render(
11 | ,
24 | );
25 | };
26 |
27 | const windowOpenSpy = jest.spyOn(window, "open");
28 |
29 | describe("TryExtensionLinkDialog", () => {
30 | test("should show scan QR box when connectDisabled is false", async () => {
31 | renderConnectDialog({ connectDisabled: false });
32 |
33 | await waitFor(() => {
34 | expect(screen.queryByTestId("connect-content")).toBeTruthy();
35 | });
36 | });
37 |
38 | test("should not show scan QR box when connectDisabled is true", async () => {
39 | renderConnectDialog({ connectDisabled: true });
40 |
41 | await waitFor(() => {
42 | expect(screen.queryByTestId("connect-content")).toBeNull();
43 | });
44 | });
45 |
46 | test("should show connecting spinner when not connected", async () => {
47 | renderConnectDialog({ isConnected: false });
48 |
49 | await waitFor(() => {
50 | expect(screen.queryByTestId("connecting-spinner")).toBeTruthy();
51 | });
52 | });
53 |
54 | test("should navigate to extension store in new tab after pressing install", async () => {
55 | const mockedWindowOpen = jest.fn();
56 | windowOpenSpy.mockImplementation(mockedWindowOpen);
57 |
58 | renderConnectDialog({});
59 |
60 | await waitFor(async () => {
61 | const button = await screen.findByRole("button", { name: "Install" });
62 | fireEvent.click(button);
63 | expect(mockedWindowOpen).toBeCalledWith(
64 | "https://api.wallet.coinbase.com/rpc/v2/desktop/chrome",
65 | "_blank",
66 | );
67 | });
68 | });
69 |
70 | test("should show refresh button after pressing install", async () => {
71 | windowOpenSpy.mockImplementation(() => null);
72 |
73 | renderConnectDialog({});
74 |
75 | await waitFor(async () => {
76 | const button = await screen.findByRole("button", { name: "Install" });
77 | expect(button.textContent).toEqual("Install");
78 |
79 | fireEvent.click(button);
80 | expect(button.textContent).toEqual("Refresh");
81 | });
82 | });
83 | });
84 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/ConnectDialog/ConnectDialog.tsx:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | import clsx from "clsx";
5 | import { h } from "preact";
6 | import { useEffect, useState } from "preact/hooks";
7 |
8 | import { ConnectContent } from "../ConnectContent/ConnectContent";
9 | import { TryExtensionContent } from "../TryExtensionContent/TryExtensionContent";
10 | import css from "./ConnectDialog-css";
11 |
12 | type ConnectDialogProps = {
13 | darkMode: boolean;
14 | version: string;
15 | sessionId: string;
16 | sessionSecret: string;
17 | linkAPIUrl: string;
18 | isOpen: boolean;
19 | isConnected: boolean;
20 | isParentConnection: boolean;
21 | chainId: number;
22 | connectDisabled: boolean;
23 | onCancel: (() => void) | null;
24 | };
25 |
26 | export const ConnectDialog = (props: ConnectDialogProps) => {
27 | const { isOpen, darkMode } = props;
28 | const [containerHidden, setContainerHidden] = useState(!isOpen);
29 | const [dialogHidden, setDialogHidden] = useState(!isOpen);
30 |
31 | useEffect(() => {
32 | const timers = [
33 | window.setTimeout(() => {
34 | setDialogHidden(!isOpen);
35 | }, 10),
36 | ];
37 |
38 | if (isOpen) {
39 | setContainerHidden(false);
40 | } else {
41 | timers.push(
42 | window.setTimeout(() => {
43 | setContainerHidden(true);
44 | }, 360),
45 | );
46 | }
47 |
48 | return () => {
49 | timers.forEach(window.clearTimeout);
50 | };
51 | }, [props.isOpen]);
52 |
53 | const theme = darkMode ? "dark" : "light";
54 |
55 | return (
56 |
62 |
63 |
70 |
71 |
77 | {!props.connectDisabled ? (
78 |
89 | ) : null}
90 |
91 |
92 |
93 |
94 | );
95 | };
96 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/ConnectDialog/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./ConnectDialog";
2 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/LinkFlow/LinkFlow.test.tsx:
--------------------------------------------------------------------------------
1 | import { render } from "@testing-library/preact";
2 | import { h } from "preact";
3 | import { Observable, Subject } from "rxjs";
4 |
5 | import { LinkFlow } from "./LinkFlow";
6 |
7 | describe("LinkFlow", () => {
8 | const linkFlow = new LinkFlow({
9 | darkMode: false,
10 | version: "1.2.1",
11 | sessionId: "session123",
12 | sessionSecret: "sessionSecret",
13 | linkAPIUrl: "http://link-url.com",
14 | isParentConnection: false,
15 | connected$: new Observable(),
16 | chainId$: new Subject(),
17 | });
18 |
19 | test("initialize", () => {
20 | expect(linkFlow).toMatchObject({
21 | connectDisabled: false,
22 | darkMode: false,
23 | isConnected: false,
24 | isOpen: false,
25 | isParentConnection: false,
26 | linkAPIUrl: "http://link-url.com",
27 | onCancel: null,
28 | root: null,
29 | sessionId: "session123",
30 | sessionSecret: "sessionSecret",
31 | version: "1.2.1",
32 | });
33 | });
34 |
35 | const attachedEl = document.getElementsByClassName("-cbwsdk-link-flow-root");
36 |
37 | describe("public methods", () => {
38 | beforeEach(() => {
39 | render();
40 | const ele = document.getElementById("attach-here");
41 | if (ele) {
42 | linkFlow.attach(ele);
43 | }
44 | });
45 |
46 | test("@attach", () => {
47 | expect(attachedEl.length).toEqual(1);
48 | });
49 |
50 | test("@detach", () => {
51 | linkFlow.detach();
52 |
53 | expect(attachedEl.length).toEqual(0);
54 | });
55 |
56 | test("@setConnectDisabled", () => {
57 | linkFlow.setConnectDisabled(true);
58 |
59 | expect(linkFlow).toMatchObject({
60 | connectDisabled: true,
61 | });
62 | });
63 |
64 | test("@open", () => {
65 | linkFlow.open({
66 | onCancel: () => {},
67 | });
68 |
69 | expect(linkFlow).toMatchObject({
70 | isOpen: true,
71 | });
72 | });
73 |
74 | test("@close", () => {
75 | linkFlow.close();
76 |
77 | expect(linkFlow).toMatchObject({
78 | isOpen: false,
79 | onCancel: null,
80 | });
81 | });
82 | });
83 |
84 | describe("without root element", () => {
85 | test("@detach", () => {
86 | const linkFlow1 = new LinkFlow({
87 | darkMode: true,
88 | version: "1.2.1",
89 | sessionId: "session123",
90 | sessionSecret: "sessionSecret",
91 | linkAPIUrl: "http://link-url.com",
92 | isParentConnection: false,
93 | connected$: new Observable(),
94 | chainId$: new Subject(),
95 | });
96 | linkFlow1.detach();
97 |
98 | expect(attachedEl.length).toEqual(0);
99 | });
100 | });
101 | });
102 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/LinkFlow/LinkFlow.tsx:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | import { h, render } from "preact";
5 | import { BehaviorSubject, Observable, Subject, Subscription } from "rxjs";
6 |
7 | import { ConnectDialog } from "../ConnectDialog/ConnectDialog";
8 |
9 | export interface LinkFlowOptions {
10 | darkMode: boolean;
11 | version: string;
12 | sessionId: string;
13 | sessionSecret: string;
14 | linkAPIUrl: string;
15 | isParentConnection: boolean;
16 | chainId$: Subject;
17 | connected$: Observable;
18 | }
19 |
20 | interface Optional {
21 | value?: T;
22 | }
23 |
24 | export class LinkFlow {
25 | private readonly darkMode: boolean;
26 | private readonly version: string;
27 | private readonly sessionId: string;
28 | private readonly sessionSecret: string;
29 | private readonly linkAPIUrl: string;
30 | private readonly isParentConnection: boolean;
31 |
32 | private readonly connected$: Observable;
33 | private readonly chainId$: Subject;
34 | private readonly extensionUI$: BehaviorSubject> =
35 | new BehaviorSubject({});
36 | private readonly subscriptions = new Subscription();
37 |
38 | private isConnected = false;
39 | private chainId = 1;
40 | private isOpen = false;
41 | private onCancel: (() => void) | null = null;
42 |
43 | private root: Element | null = null;
44 |
45 | // if true, hide QR code in LinkFlow (which happens if no jsonRpcUrl is provided)
46 | private connectDisabled = false;
47 |
48 | constructor(options: Readonly) {
49 | this.darkMode = options.darkMode;
50 | this.version = options.version;
51 | this.sessionId = options.sessionId;
52 | this.sessionSecret = options.sessionSecret;
53 | this.linkAPIUrl = options.linkAPIUrl;
54 | this.isParentConnection = options.isParentConnection;
55 | this.connected$ = options.connected$;
56 | this.chainId$ = options.chainId$;
57 | }
58 |
59 | public attach(el: Element): void {
60 | this.root = document.createElement("div");
61 | this.root.className = "-cbwsdk-link-flow-root";
62 | el.appendChild(this.root);
63 | this.render();
64 |
65 | this.subscriptions.add(
66 | this.connected$.subscribe(v => {
67 | if (this.isConnected !== v) {
68 | this.isConnected = v;
69 | this.render();
70 | }
71 | }),
72 | );
73 |
74 | this.subscriptions.add(
75 | this.chainId$.subscribe(chainId => {
76 | if (this.chainId !== chainId) {
77 | this.chainId = chainId;
78 | this.render();
79 | }
80 | }),
81 | );
82 | }
83 |
84 | public detach(): void {
85 | if (!this.root) {
86 | return;
87 | }
88 | this.subscriptions.unsubscribe();
89 | render(null, this.root);
90 | this.root.parentElement?.removeChild(this.root);
91 | }
92 |
93 | public setConnectDisabled(connectDisabled: boolean) {
94 | this.connectDisabled = connectDisabled;
95 | }
96 |
97 | public open(options: { onCancel: () => void }): void {
98 | this.isOpen = true;
99 | this.onCancel = options.onCancel;
100 | this.render();
101 | }
102 |
103 | public close(): void {
104 | this.isOpen = false;
105 | this.onCancel = null;
106 | this.render();
107 | }
108 |
109 | private render(): void {
110 | if (!this.root) {
111 | return;
112 | }
113 |
114 | const subscription = this.extensionUI$.subscribe(() => {
115 | if (!this.root) {
116 | return;
117 | }
118 |
119 | render(
120 | ,
133 | this.root,
134 | );
135 | });
136 |
137 | this.subscriptions.add(subscription);
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/LinkFlow/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./LinkFlow";
2 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/QRCode.tsx:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | import { FunctionComponent, h } from "preact";
5 | import { useEffect, useState } from "preact/hooks";
6 |
7 | import QRCodeSVG from "../vendor-js/qrcode-svg";
8 |
9 | export interface QRCodeProps {
10 | content: string;
11 | width?: number;
12 | height?: number;
13 | fgColor?: string;
14 | bgColor?: string;
15 | image?: QRCodeSVG.SvgLogo;
16 | }
17 |
18 | export const QRCode: FunctionComponent = props => {
19 | const [svg, setSvg] = useState("");
20 |
21 | useEffect(() => {
22 | const qrcode = new QRCodeSVG({
23 | content: props.content,
24 | background: props.bgColor || "#ffffff",
25 | color: props.fgColor || "#000000",
26 | container: "svg",
27 | ecl: "M",
28 | width: props.width ?? 256,
29 | height: props.height ?? 256,
30 | padding: 0,
31 | image: props.image,
32 | });
33 | const base64 = Buffer.from(qrcode.svg(), "utf8").toString("base64");
34 | setSvg(`data:image/svg+xml;base64,${base64}`);
35 | });
36 |
37 | return svg ?
: null;
38 | };
39 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/Snackbar/Snackbar.scss:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | .-cbwsdk-css-reset {
5 | .-gear-container {
6 | margin-left: 16px !important;
7 | margin-right: 9px !important;
8 | display: flex;
9 | align-items: center;
10 | justify-content: center;
11 | width: 24px;
12 | height: 24px;
13 | transition: opacity 0.25s;
14 |
15 | * {
16 | user-select: none;
17 | }
18 |
19 | svg {
20 | opacity: 0;
21 | position: absolute;
22 | }
23 | }
24 |
25 | .-gear-icon {
26 | height: 12px;
27 | width: 12px;
28 | z-index: 10000;
29 | }
30 |
31 | .-cbwsdk-snackbar {
32 | align-items: flex-end;
33 | display: flex;
34 | flex-direction: column;
35 | position: fixed;
36 | right: 0;
37 | top: 0;
38 | z-index: 2147483647;
39 |
40 | * {
41 | user-select: none;
42 | }
43 |
44 | &-instance {
45 | display: flex;
46 | flex-direction: column;
47 | margin: 8px 16px 0 16px;
48 | overflow: visible;
49 | text-align: left;
50 | transform: translateX(0);
51 | transition: opacity 0.25s, transform 0.25s;
52 |
53 | &-header:hover {
54 | .-gear-container svg {
55 | opacity: 1;
56 | }
57 | }
58 |
59 | &-header {
60 | display: flex;
61 | align-items: center;
62 | background: #ffffff;
63 | overflow: hidden;
64 |
65 | border: 1px solid #e7ebee;
66 | box-sizing: border-box;
67 | border-radius: 8px;
68 | cursor: pointer;
69 |
70 | &-cblogo {
71 | margin: 8px 8px 8px 8px;
72 | }
73 |
74 | * {
75 | cursor: pointer;
76 | }
77 |
78 | &-message {
79 | color: #000;
80 | font-size: 13px;
81 | line-height: 1.5;
82 | user-select: none;
83 | }
84 | }
85 |
86 | &-menu {
87 | background: #ffffff;
88 |
89 | transition: opacity 0.25s ease-in-out, transform 0.25s linear,
90 | visibility 0s;
91 | visibility: hidden;
92 |
93 | border: 1px solid #e7ebee;
94 | box-sizing: border-box;
95 | border-radius: 8px;
96 |
97 | opacity: 0;
98 | flex-direction: column;
99 | padding-left: 8px;
100 | padding-right: 8px;
101 |
102 | &-item:last-child {
103 | margin-bottom: 8px !important;
104 | }
105 |
106 | &-item:hover {
107 | background: #f5f7f8;
108 | border-radius: 6px;
109 | transition: background 0.25s;
110 |
111 | span {
112 | color: #050f19;
113 | transition: color 0.25s;
114 | }
115 |
116 | svg path {
117 | fill: #000000;
118 | transition: fill 0.25s;
119 | }
120 | }
121 |
122 | &-item {
123 | visibility: inherit;
124 | height: 35px;
125 | margin-top: 8px;
126 | margin-bottom: 0;
127 | display: flex;
128 | flex-direction: row;
129 | align-items: center;
130 | padding: 8px;
131 | cursor: pointer;
132 |
133 | * {
134 | visibility: inherit;
135 | cursor: pointer;
136 | }
137 |
138 | &-is-red:hover {
139 | background: rgba(223, 95, 103, 0.2);
140 | transition: background 0.25s;
141 |
142 | * {
143 | cursor: pointer;
144 | }
145 |
146 | svg path {
147 | fill: #df5f67;
148 | transition: fill 0.25s;
149 | }
150 |
151 | span {
152 | color: #df5f67;
153 | transition: color 0.25s;
154 | }
155 | }
156 |
157 | &-info {
158 | color: #aaaaaa;
159 | font-size: 13px;
160 | margin: 0 8px 0 32px;
161 | position: absolute;
162 | }
163 | }
164 | }
165 |
166 | &-hidden {
167 | opacity: 0;
168 | text-align: left;
169 | transform: translateX(25%);
170 | transition: opacity 0.5s linear;
171 | }
172 |
173 | &-expanded {
174 | .-cbwsdk-snackbar-instance-menu {
175 | opacity: 1;
176 | display: flex;
177 | transform: translateY(8px);
178 | visibility: visible;
179 | }
180 | }
181 | }
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/Snackbar/Snackbar.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen, waitFor } from "@testing-library/preact";
2 | import { h } from "preact";
3 |
4 | import { Snackbar } from "./Snackbar";
5 |
6 | const attachedEl = document.getElementsByClassName("-cbwsdk-snackbar-root");
7 |
8 | describe("Snackbar", () => {
9 | const snackbar = new Snackbar({
10 | darkMode: false,
11 | });
12 |
13 | beforeEach(() => {
14 | render();
15 | const ele = document.getElementById("attach-here");
16 | if (ele) {
17 | snackbar.attach(ele);
18 | }
19 | });
20 |
21 | describe("public methods", () => {
22 | test("@attach", () => {
23 | expect(attachedEl.length).toEqual(1);
24 | });
25 |
26 | test("@presentItem", async () => {
27 | snackbar.presentItem({
28 | message: "Confirm on phone",
29 | appSrc: "coinbase-wallet",
30 | menuItems: [
31 | {
32 | isRed: true,
33 | info: "Cancel transaction",
34 | svgWidth: "11",
35 | svgHeight: "11",
36 | path: "",
37 | defaultFillRule: "inherit",
38 | defaultClipRule: "inherit",
39 | onClick: jest.fn,
40 | },
41 | {
42 | isRed: true,
43 | info: "Reset connection",
44 | svgWidth: "10",
45 | svgHeight: "11",
46 | path: "",
47 | defaultFillRule: "evenodd",
48 | defaultClipRule: "evenodd",
49 | onClick: jest.fn,
50 | },
51 | ],
52 | });
53 |
54 | await waitFor(() => {
55 | expect(screen.queryByText("Cancel transaction")).toBeInTheDocument();
56 | expect(screen.queryByText("Reset connection")).toBeInTheDocument();
57 | expect(
58 | document.getElementsByClassName(
59 | "-cbwsdk-snackbar-instance-menu-item-info-is-red",
60 | ).length,
61 | ).toEqual(2);
62 | });
63 | });
64 |
65 | test("@clear", () => {
66 | const menuItems = document.getElementsByClassName(
67 | "-cbwsdk-snackbar-instance-menu",
68 | );
69 | expect(menuItems.length).toEqual(1);
70 | snackbar.clear();
71 | expect(menuItems.length).toEqual(0);
72 | });
73 | });
74 | });
75 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/Snackbar/SnackbarContainer.test.tsx:
--------------------------------------------------------------------------------
1 | import { fireEvent, render, screen } from "@testing-library/preact";
2 | import { h } from "preact";
3 |
4 | import {
5 | SnackbarContainer,
6 | SnackbarInstance,
7 | SnackbarInstanceProps,
8 | } from "./Snackbar";
9 |
10 | const renderSnackbarContainer = (props?: SnackbarInstanceProps) =>
11 | render(
12 |
13 |
14 | ,
15 | );
16 |
17 | describe("SnackbarContainer", () => {
18 | beforeEach(() => {
19 | jest.useFakeTimers();
20 | jest.spyOn(window, "setTimeout");
21 | renderSnackbarContainer({
22 | menuItems: [
23 | {
24 | isRed: true,
25 | info: "Cancel transaction",
26 | svgWidth: "11",
27 | svgHeight: "11",
28 | path: "",
29 | defaultFillRule: "inherit",
30 | defaultClipRule: "inherit",
31 | onClick: jest.fn,
32 | },
33 | ],
34 | });
35 | });
36 |
37 | afterEach(() => {
38 | jest.useRealTimers();
39 | });
40 |
41 | test("render hidden", () => {
42 | const hiddenClass = document.getElementsByClassName(
43 | "-cbwsdk-snackbar-instance-hidden",
44 | );
45 | expect(hiddenClass.length).toEqual(1);
46 | jest.runAllTimers();
47 | expect(setTimeout).toHaveBeenCalledTimes(2);
48 | });
49 |
50 | test("toggle expand", () => {
51 | const header = document.getElementsByClassName(
52 | "-cbwsdk-snackbar-instance-header",
53 | )[0];
54 | const expandedClass = document.getElementsByClassName(
55 | "-cbwsdk-snackbar-instance-expanded",
56 | );
57 | fireEvent.click(header);
58 | expect(expandedClass.length).toEqual(1);
59 | expect(screen.queryByText("Cancel transaction")).toBeVisible();
60 | });
61 | });
62 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/Snackbar/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./Snackbar";
2 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/Spinner/Spinner.scss:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | .-cbwsdk-css-reset {
5 | .-cbwsdk-spinner {
6 | display: inline-block;
7 |
8 | svg {
9 | display: inline-block;
10 | animation: 2s linear infinite -cbwsdk-spinner-svg;
11 | circle {
12 | animation: 1.9s ease-in-out infinite both -cbwsdk-spinner-circle;
13 | display: block;
14 | fill: transparent;
15 | stroke-dasharray: 283;
16 | stroke-dashoffset: 280;
17 | stroke-linecap: round;
18 | stroke-width: 10px;
19 | transform-origin: 50% 50%;
20 | }
21 | }
22 | }
23 | }
24 |
25 | @keyframes -cbwsdk-spinner-svg {
26 | 0% {
27 | transform: rotateZ(0deg);
28 | }
29 | 100% {
30 | transform: rotateZ(360deg);
31 | }
32 | }
33 |
34 | @keyframes -cbwsdk-spinner-circle {
35 | 0%,
36 | 25% {
37 | stroke-dashoffset: 280;
38 | transform: rotate(0);
39 | }
40 |
41 | 50%,
42 | 75% {
43 | stroke-dashoffset: 75;
44 | transform: rotate(45deg);
45 | }
46 |
47 | 100% {
48 | stroke-dashoffset: 280;
49 | transform: rotate(360deg);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/Spinner/Spinner.test.tsx:
--------------------------------------------------------------------------------
1 | import { render } from "@testing-library/preact";
2 | import { h } from "preact";
3 |
4 | import { Spinner } from "./Spinner";
5 |
6 | const renderSpinner = (props: { size?: number; color?: string }) =>
7 | render();
8 |
9 | describe("Spinner", () => {
10 | test("renders default", () => {
11 | renderSpinner({});
12 |
13 | const svgStyle = document.querySelector("svg")?.style;
14 | const svgCircle = document.querySelector("circle")?.style;
15 |
16 | expect(svgStyle?.width).toEqual("64px");
17 | expect(svgStyle?.height).toEqual("64px");
18 | expect(svgCircle?.stroke).toEqual("#000");
19 | });
20 |
21 | test("renders overrides", () => {
22 | renderSpinner({
23 | size: 200,
24 | color: "red",
25 | });
26 |
27 | const svgStyle = document.querySelector("svg")?.style;
28 | const svgCircle = document.querySelector("circle")?.style;
29 |
30 | expect(svgStyle?.width).toEqual("200px");
31 | expect(svgStyle?.height).toEqual("200px");
32 | expect(svgCircle?.stroke).toEqual("red");
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/Spinner/Spinner.tsx:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | import { FunctionComponent, h } from "preact";
5 |
6 | import css from "./Spinner-css";
7 |
8 | export const Spinner: FunctionComponent<{
9 | size?: number;
10 | color?: string;
11 | }> = props => {
12 | const size = props.size ?? 64;
13 | const color = props.color || "#000";
14 |
15 | return (
16 |
17 |
18 |
25 |
26 | );
27 | };
28 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/Spinner/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./Spinner";
2 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/TryExtensionContent/TryExtensionContent.scss:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | @import "../theme.scss";
5 |
6 | .-cbwsdk-css-reset {
7 | .-cbwsdk-try-extension {
8 | display: flex;
9 | margin-top: 12px;
10 | height: 202px;
11 | width: 700px;
12 | border-radius: 12px;
13 | padding: 30px;
14 |
15 | &.light {
16 | background: $white;
17 | }
18 |
19 | &.dark {
20 | background: $black;
21 | }
22 |
23 | &-column-half {
24 | flex: 50%;
25 | }
26 |
27 | &-heading {
28 | font-style: normal;
29 | font-weight: 500;
30 | font-size: 25px;
31 | line-height: 32px;
32 | margin: 0;
33 | max-width: 204px;
34 |
35 | &.light {
36 | color: $black;
37 | }
38 |
39 | &.dark {
40 | color: $white;
41 | }
42 | }
43 |
44 | &-cta {
45 | appearance: none;
46 | border: none;
47 | background: none;
48 | color: $primary;
49 | cursor: pointer;
50 | padding: 0;
51 | text-decoration: none;
52 | display: block;
53 | font-weight: 600;
54 | font-size: 16px;
55 | line-height: 24px;
56 |
57 | &.light {
58 | color: $primary;
59 | }
60 |
61 | &.dark {
62 | color: $primary-dark;
63 | }
64 | }
65 |
66 | &-cta-wrapper {
67 | display: flex;
68 | align-items: center;
69 | margin-top: 12px;
70 | }
71 |
72 | &-cta-icon {
73 | display: block;
74 | margin-left: 4px;
75 | height: 14px;
76 | }
77 |
78 | &-list {
79 | display: flex;
80 | flex-direction: column;
81 | justify-content: center;
82 | align-items: center;
83 | margin: 0;
84 | padding: 0;
85 | list-style: none;
86 | height: 100%;
87 | }
88 |
89 | &-list-item {
90 | display: flex;
91 | align-items: center;
92 | flex-flow: nowrap;
93 | margin-top: 24px;
94 |
95 | &:first-of-type {
96 | margin-top: 0;
97 | }
98 | }
99 |
100 | &-list-item-icon-wrapper {
101 | display: block;
102 | }
103 |
104 | &-list-item-icon {
105 | display: flex;
106 | height: 32px;
107 | width: 32px;
108 | border-radius: 50%;
109 |
110 | svg {
111 | margin: auto;
112 | display: block;
113 | }
114 |
115 | &.light {
116 | background: $background-alt;
117 | }
118 |
119 | &.dark {
120 | background: $background-alt-dark;
121 | }
122 | }
123 |
124 | &-list-item-copy {
125 | display: block;
126 | font-weight: 400;
127 | font-size: 14px;
128 | line-height: 20px;
129 | padding-left: 12px;
130 |
131 | &.light {
132 | color: $foreground-muted;
133 | }
134 |
135 | &.dark {
136 | color: $foreground-muted-dark;
137 | }
138 | }
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/TryExtensionContent/TryExtensionContent.tsx:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | import clsx from "clsx";
5 | import { h } from "preact";
6 | import { useCallback, useState } from "preact/hooks";
7 |
8 | import { ArrowLeftIcon } from "../icons/ArrowLeftIcon";
9 | import { LaptopIcon } from "../icons/LaptopIcon";
10 | import { SafeIcon } from "../icons/SafeIcon";
11 | import { Theme } from "../types";
12 | import css from "./TryExtensionContent-css";
13 |
14 | type TryExtensionContentProps = {
15 | theme: Theme;
16 | };
17 |
18 | export function TryExtensionContent({ theme }: TryExtensionContentProps) {
19 | const [clicked, setClicked] = useState(false);
20 |
21 | const handleInstallClick = useCallback(() => {
22 | window.open(
23 | "https://api.wallet.coinbase.com/rpc/v2/desktop/chrome",
24 | "_blank",
25 | );
26 | }, []);
27 |
28 | const handleClick = useCallback(() => {
29 | if (clicked) {
30 | window.location.reload();
31 | } else {
32 | handleInstallClick();
33 | setClicked(true);
34 | }
35 | }, [handleInstallClick, clicked]);
36 |
37 | return (
38 |
39 |
40 |
41 |
42 | Or try the Coinbase Wallet browser extension
43 |
44 |
45 |
51 |
52 | {!clicked && (
53 |
57 | )}
58 |
59 |
60 |
61 |
86 |
87 | );
88 | }
89 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/TryExtensionContent/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./TryExtensionContent";
2 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/icons/ArrowLeftIcon.tsx:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 |
3 | export function ArrowLeftIcon(props: h.JSX.SVGAttributes) {
4 | return (
5 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/icons/CloseIcon.tsx:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 |
3 | export function CloseIcon(props: h.JSX.SVGAttributes) {
4 | return (
5 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/icons/LaptopIcon.tsx:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 |
3 | export function LaptopIcon(props: h.JSX.SVGAttributes) {
4 | return (
5 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/icons/QRCodeIcon.tsx:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 |
3 | export function QRCodeIcon(props: h.JSX.SVGAttributes) {
4 | return (
5 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/icons/QRLogoCoinbase.tsx:
--------------------------------------------------------------------------------
1 | const svg = `
2 |
7 | `;
8 |
9 | export default svg;
10 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/icons/QRLogoWallet.ts:
--------------------------------------------------------------------------------
1 | export default `
2 |
8 | `;
9 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/icons/SafeIcon.tsx:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 |
3 | export function SafeIcon(props: h.JSX.SVGAttributes) {
4 | return (
5 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/icons/StatusDotIcon.tsx:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 |
3 | export function StatusDotIcon(props: h.JSX.SVGAttributes) {
4 | return (
5 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/icons/coinbase-round.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/icons/coinbase-wallet-round.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/icons/coinbase.svg:
--------------------------------------------------------------------------------
1 |
21 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/theme.scss:
--------------------------------------------------------------------------------
1 | $primary: #0052ff;
2 | $primary-dark: #588af5;
3 |
4 | $primary-wash: #f5f8ff;
5 | $primary-wash-dark: #001033;
6 |
7 | $foreground-muted: #5b636e;
8 | $foreground-muted-dark: #8a919e;
9 |
10 | $white: #fff;
11 | $black: #0a0b0d;
12 |
13 | $background: $white;
14 | $background-alt: #eef0f3;
15 | $background-dark: $black;
16 | $background-alt-dark: #1e2025;
17 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/components/types.ts:
--------------------------------------------------------------------------------
1 | export type Theme = "dark" | "light";
2 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/connection/ClientMessage.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | import { IntNumber } from "../types";
5 |
6 | export interface ClientMessage {
7 | type: string;
8 | id: IntNumber;
9 | }
10 |
11 | export interface ClientMessageHostSession extends ClientMessage {
12 | type: "HostSession";
13 | sessionId: string;
14 | sessionKey: string;
15 | }
16 |
17 | export function ClientMessageHostSession(
18 | params: Omit,
19 | ): ClientMessageHostSession {
20 | return { type: "HostSession", ...params };
21 | }
22 |
23 | export interface ClientMessageIsLinked extends ClientMessage {
24 | type: "IsLinked";
25 | sessionId: string;
26 | }
27 |
28 | export function ClientMessageIsLinked(
29 | params: Omit,
30 | ): ClientMessageIsLinked {
31 | return { type: "IsLinked", ...params };
32 | }
33 |
34 | export interface ClientMessageGetSessionConfig extends ClientMessage {
35 | type: "GetSessionConfig";
36 | sessionId: string;
37 | }
38 |
39 | export function ClientMessageGetSessionConfig(
40 | params: Omit,
41 | ): ClientMessageGetSessionConfig {
42 | return { type: "GetSessionConfig", ...params };
43 | }
44 |
45 | export interface ClientMessageSetSessionConfig extends ClientMessage {
46 | type: "SetSessionConfig";
47 | id: IntNumber;
48 | sessionId: string;
49 | webhookId?: string | null;
50 | webhookUrl?: string | null;
51 | metadata?: { [key: string]: string | null };
52 | }
53 |
54 | export function ClientMessageSetSessionConfig(
55 | params: Omit,
56 | ): ClientMessageSetSessionConfig {
57 | return { type: "SetSessionConfig", ...params };
58 | }
59 |
60 | export interface ClientMessagePublishEvent extends ClientMessage {
61 | type: "PublishEvent";
62 | sessionId: string;
63 | event: string;
64 | data: string;
65 | callWebhook: boolean;
66 | }
67 |
68 | export function ClientMessagePublishEvent(
69 | params: Omit,
70 | ): ClientMessagePublishEvent {
71 | return { type: "PublishEvent", ...params };
72 | }
73 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/connection/DiagnosticLogger.test.ts:
--------------------------------------------------------------------------------
1 | import {
2 | DiagnosticLogger,
3 | EVENTS,
4 | EventType,
5 | LogProperties,
6 | } from "./DiagnosticLogger";
7 |
8 | describe("DiagnosticLogger", () => {
9 | describe("log", () => {
10 | let event: string;
11 | let props: LogProperties | undefined;
12 |
13 | class diagnosticLogger implements DiagnosticLogger {
14 | log(eventType: EventType, Properties?: LogProperties) {
15 | event = eventType;
16 | props = Properties;
17 | }
18 | }
19 | const diagnostic = new diagnosticLogger();
20 | test("calls the log function", () => {
21 | const logProperties = {
22 | message: "a message",
23 | value: "a value",
24 | };
25 | diagnostic.log(EVENTS.CONNECTED_STATE_CHANGE, logProperties);
26 |
27 | expect(event).toEqual(EVENTS.CONNECTED_STATE_CHANGE);
28 | expect(props).toEqual(logProperties);
29 | });
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/connection/DiagnosticLogger.ts:
--------------------------------------------------------------------------------
1 | // DiagnosticLogger for debugging purposes only
2 |
3 | import { Web3RequestMessage } from "../relay/Web3RequestMessage";
4 | import { Web3ResponseMessage } from "../relay/Web3ResponseMessage";
5 | import { ConnectionState } from "./RxWebSocket";
6 | import { ServerMessage, ServerMessageIsLinkedOK } from "./ServerMessage";
7 |
8 | export type LogProperties = {
9 | addresses_length?: number; // number of eth addresses
10 | alreadyDestroyed?: boolean; // error flag if metadata is already destroyed on resetAndReload
11 | eventId?: Web3RequestMessage["id"] | Web3ResponseMessage["id"];
12 | isSessionMismatched?: string; // storedSession does not match sessionId
13 | linked?: ServerMessageIsLinkedOK["linked"];
14 | message?: string; // error message
15 | metadata_keys?: string[]; // session config metadata keys
16 | method?: string; // method throwing error message
17 | onlineGuests?: number; // number of online guests (should be 0 or 1)
18 | sessionIdHash?: string; // anonymous session id for debugging specific sessions
19 | sessionMetadataChange?: string; // change in session metadata
20 | state?: ConnectionState;
21 | storedSessionIdHash?: string; // anonymous session id from localStorage
22 | type?: ServerMessage["type"];
23 | value?: string; // error value associated with the message
24 | };
25 |
26 | type Keys = keyof typeof EVENTS;
27 | export type EventType = typeof EVENTS[Keys];
28 |
29 | export interface DiagnosticLogger {
30 | log(eventType: EventType, logProperties?: LogProperties): void;
31 | }
32 |
33 | export const EVENTS = {
34 | STARTED_CONNECTING: "walletlink_sdk.started.connecting",
35 | CONNECTED_STATE_CHANGE: "walletlink_sdk.connected",
36 | DISCONNECTED: "walletlink_sdk.disconnected",
37 | METADATA_DESTROYED: "walletlink_sdk_metadata_destroyed",
38 | LINKED: "walletlink_sdk.linked",
39 | FAILURE: "walletlink_sdk.generic_failure",
40 | SESSION_CONFIG_RECEIVED: "walletlink_sdk.session_config_event_received",
41 | ETH_ACCOUNTS_STATE: "walletlink_sdk.eth_accounts_state",
42 | SESSION_STATE_CHANGE: "walletlink_sdk.session_state_change",
43 | UNLINKED_ERROR_STATE: "walletlink_sdk.unlinked_error_state",
44 | SKIPPED_CLEARING_SESSION: "walletlink_sdk.skipped_clearing_session",
45 | GENERAL_ERROR: "walletlink_sdk.general_error",
46 | WEB3_REQUEST: "walletlink_sdk.web3.request",
47 | WEB3_REQUEST_PUBLISHED: "walletlink_sdk.web3.request_published",
48 | WEB3_RESPONSE: "walletlink_sdk.web3.response",
49 | UNKNOWN_ADDRESS_ENCOUNTERED: "walletlink_sdk.unknown_address_encountered",
50 | };
51 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/connection/EventListener.ts:
--------------------------------------------------------------------------------
1 | /** @deprecated in favor of DiagnosticLogger */
2 | export interface EventListener {
3 | onEvent(eventType: string, eventProperties?: any): void;
4 | }
5 |
6 | export const EVENTS = {
7 | STARTED_CONNECTING: "walletlink_sdk.started.connecting",
8 | CONNECTED_STATE_CHANGE: "walletlink_sdk.connected",
9 | DISCONNECTED: "walletlink_sdk.disconnected",
10 | METADATA_DESTROYED: "walletlink_sdk_metadata_destroyed",
11 | LINKED: "walletlink_sdk.linked",
12 | FAILURE: "walletlink_sdk.generic_failure",
13 | SESSION_CONFIG_RECEIVED: "walletlink_sdk.session_config_event_received",
14 | ETH_ACCOUNTS_STATE: "walletlink_sdk.eth_accounts_state",
15 | SESSION_STATE_CHANGE: "walletlink_sdk.session_state_change",
16 | UNLINKED_ERROR_STATE: "walletlink_sdk.unlinked_error_state",
17 | SKIPPED_CLEARING_SESSION: "walletlink_sdk.skipped_clearing_session",
18 | GENERAL_ERROR: "walletlink_sdk.general_error",
19 | WEB3_REQUEST: "walletlink_sdk.web3.request",
20 | WEB3_REQUEST_PUBLISHED: "walletlink_sdk.web3.request_published",
21 | WEB3_RESPONSE: "walletlink_sdk.web3.response",
22 | UNKNOWN_ADDRESS_ENCOUNTERED: "walletlink_sdk.unknown_address_encountered",
23 | };
24 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/connection/RxWebSocket.test.ts:
--------------------------------------------------------------------------------
1 | import WS from "jest-websocket-mock";
2 | import { Observable } from "rxjs";
3 |
4 | import { ConnectionState, RxWebSocket } from "./RxWebSocket";
5 |
6 | describe("RxWebSocket", () => {
7 | let server: WS;
8 | let rxWS: RxWebSocket;
9 | beforeEach(() => {
10 | server = new WS("ws://localhost:1234");
11 | rxWS = new RxWebSocket("http://localhost:1234");
12 | });
13 |
14 | afterEach(() => {
15 | WS.clean();
16 | });
17 |
18 | describe("is connected", () => {
19 | test("@connect & @disconnect", async () => {
20 | const client = rxWS.connect();
21 |
22 | expect(client).toBeInstanceOf(Observable);
23 | await client.toPromise();
24 | await server.connected;
25 |
26 | // @ts-expect-error test private methods
27 | expect(rxWS.webSocket).toBeInstanceOf(WebSocket);
28 | // @ts-expect-error test private methods
29 | rxWS.connectionStateSubject
30 | .subscribe({
31 | next: val => {
32 | // Connected state
33 | expect(val).toEqual(ConnectionState.CONNECTED);
34 | },
35 | closed: (val: ConnectionState) => {
36 | // Disconnected state
37 | expect(val).toEqual(ConnectionState.DISCONNECTED);
38 | },
39 | })
40 | .unsubscribe();
41 |
42 | // Sends data
43 | const webSocketSendMock = jest
44 | .spyOn(WebSocket.prototype, "send")
45 | .mockImplementation(() => {});
46 |
47 | rxWS.sendData("data");
48 | expect(webSocketSendMock).toHaveBeenCalledWith("data");
49 |
50 | // Disconnects
51 | rxWS.disconnect();
52 | // @ts-expect-error test private methods
53 | expect(rxWS.webSocket).toBe(null);
54 | });
55 |
56 | test("@connectionState$ & @incomingData$", () => {
57 | expect(rxWS.connectionState$).toBeInstanceOf(Observable);
58 | expect(rxWS.incomingData$).toBeInstanceOf(Observable);
59 | });
60 |
61 | test("@incomingJSONData$", () => {
62 | expect(rxWS.incomingJSONData$).toBeInstanceOf(Observable);
63 | });
64 |
65 | describe("errors & event listeners", () => {
66 | afterEach(() => rxWS.disconnect());
67 |
68 | test("@connect throws error when connecting again", async () => {
69 | const client = rxWS.connect();
70 | await client.toPromise();
71 |
72 | await expect(rxWS.connect().toPromise()).rejects.toThrow(
73 | "webSocket object is not null",
74 | );
75 | });
76 |
77 | test("@connect throws error & fails to set websocket instance", async () => {
78 | const errorConnect = new RxWebSocket("");
79 |
80 | await expect(errorConnect.connect().toPromise()).rejects.toThrow(
81 | "Failed to construct 'WebSocket': 1 argument required, but only 0 present.",
82 | );
83 | });
84 |
85 | test("onclose event throws error", async () => {
86 | const client = rxWS.connect();
87 | await client.toPromise();
88 | await server.connected;
89 | server.error();
90 |
91 | await expect(rxWS.connect().toPromise()).rejects.toThrow(
92 | "websocket error 1000: ",
93 | );
94 | });
95 |
96 | test("onmessage event emits message", async () => {
97 | const client = rxWS.connect();
98 | await client.toPromise();
99 | await server.connected;
100 |
101 | // @ts-expect-error test private methods
102 | rxWS.incomingDataSubject.subscribe(val =>
103 | expect(val).toEqual("hello world"),
104 | );
105 | server.send("hello world");
106 | });
107 | });
108 | });
109 |
110 | describe("is not connected", () => {
111 | test("sendData error", () => {
112 | expect(() => rxWS.sendData("data")).toThrowError(
113 | "websocket is not connected",
114 | );
115 | });
116 |
117 | test("disconnect returns", () => {
118 | const webSocketCloseMock = jest
119 | .spyOn(WebSocket.prototype, "close")
120 | .mockImplementation(() => {});
121 |
122 | rxWS.disconnect();
123 | expect(webSocketCloseMock).not.toBeCalled();
124 | });
125 | });
126 | });
127 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/connection/RxWebSocket.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | import {
5 | BehaviorSubject,
6 | empty,
7 | Observable,
8 | of,
9 | Subject,
10 | throwError,
11 | } from "rxjs";
12 | import { flatMap, take } from "rxjs/operators";
13 |
14 | export enum ConnectionState {
15 | DISCONNECTED,
16 | CONNECTING,
17 | CONNECTED,
18 | }
19 |
20 | /**
21 | * Rx-ified WebSocket
22 | */
23 | export class RxWebSocket {
24 | private readonly url: string;
25 | private webSocket: WebSocket | null = null;
26 | private connectionStateSubject = new BehaviorSubject(
27 | ConnectionState.DISCONNECTED,
28 | );
29 | private incomingDataSubject = new Subject();
30 |
31 | /**
32 | * Constructor
33 | * @param url WebSocket server URL
34 | * @param [WebSocketClass] Custom WebSocket implementation
35 | */
36 | constructor(
37 | url: string,
38 | private readonly WebSocketClass: typeof WebSocket = WebSocket,
39 | ) {
40 | this.url = url.replace(/^http/, "ws");
41 | }
42 |
43 | /**
44 | * Make a websocket connection
45 | * @returns an Observable that completes when connected
46 | */
47 | public connect(): Observable {
48 | if (this.webSocket) {
49 | return throwError(new Error("webSocket object is not null"));
50 | }
51 | return new Observable(obs => {
52 | let webSocket: WebSocket;
53 | try {
54 | this.webSocket = webSocket = new this.WebSocketClass(this.url);
55 | } catch (err) {
56 | obs.error(err);
57 | return;
58 | }
59 | this.connectionStateSubject.next(ConnectionState.CONNECTING);
60 | webSocket.onclose = evt => {
61 | this.clearWebSocket();
62 | obs.error(new Error(`websocket error ${evt.code}: ${evt.reason}`));
63 | this.connectionStateSubject.next(ConnectionState.DISCONNECTED);
64 | };
65 | webSocket.onopen = _ => {
66 | obs.next();
67 | obs.complete();
68 | this.connectionStateSubject.next(ConnectionState.CONNECTED);
69 | };
70 | webSocket.onmessage = evt => {
71 | this.incomingDataSubject.next(evt.data as string);
72 | };
73 | }).pipe(take(1));
74 | }
75 |
76 | /**
77 | * Disconnect from server
78 | */
79 | public disconnect(): void {
80 | const { webSocket } = this;
81 | if (!webSocket) {
82 | return;
83 | }
84 | this.clearWebSocket();
85 | this.connectionStateSubject.next(ConnectionState.DISCONNECTED);
86 | try {
87 | webSocket.close();
88 | } catch {}
89 | }
90 |
91 | /**
92 | * Emit current connection state and subsequent changes
93 | * @returns an Observable for the connection state
94 | */
95 | public get connectionState$(): Observable {
96 | return this.connectionStateSubject.asObservable();
97 | }
98 |
99 | /**
100 | * Emit incoming data from server
101 | * @returns an Observable for the data received
102 | */
103 | public get incomingData$(): Observable {
104 | return this.incomingDataSubject.asObservable();
105 | }
106 |
107 | /**
108 | * Emit incoming JSON data from server. non-JSON data are ignored
109 | * @returns an Observable for parsed JSON data
110 | */
111 | public get incomingJSONData$(): Observable {
112 | return this.incomingData$.pipe(
113 | flatMap(m => {
114 | let j: any;
115 | try {
116 | j = JSON.parse(m);
117 | } catch (err) {
118 | return empty();
119 | }
120 | return of(j);
121 | }),
122 | );
123 | }
124 |
125 | /**
126 | * Send data to server
127 | * @param data text to send
128 | */
129 | public sendData(data: string): void {
130 | const { webSocket } = this;
131 | if (!webSocket) {
132 | throw new Error("websocket is not connected");
133 | }
134 | webSocket.send(data);
135 | }
136 |
137 | private clearWebSocket(): void {
138 | const { webSocket } = this;
139 | if (!webSocket) {
140 | return;
141 | }
142 | this.webSocket = null;
143 | webSocket.onclose = null;
144 | webSocket.onerror = null;
145 | webSocket.onmessage = null;
146 | webSocket.onopen = null;
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/connection/ServerMessage.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | import { IntNumber } from "../types";
5 |
6 | export interface ServerMessage {
7 | type: string;
8 | id?: IntNumber;
9 | }
10 |
11 | export interface ServerMessageOK extends ServerMessage {
12 | type: "OK";
13 | id: IntNumber;
14 | sessionId: string;
15 | }
16 |
17 | export interface ServerMessageFail extends ServerMessage {
18 | type: "Fail";
19 | id: IntNumber;
20 | sessionId: string;
21 | error: string;
22 | }
23 |
24 | export function isServerMessageFail(msg: any): msg is ServerMessageFail {
25 | return (
26 | msg &&
27 | msg.type === "Fail" &&
28 | typeof msg.id === "number" &&
29 | typeof msg.sessionId === "string" &&
30 | typeof msg.error === "string"
31 | );
32 | }
33 |
34 | export interface ServerMessageIsLinkedOK extends ServerMessage {
35 | type: "IsLinkedOK";
36 | id: IntNumber;
37 | sessionId: string;
38 | linked: boolean;
39 | onlineGuests: number;
40 | }
41 |
42 | export interface ServerMessageLinked extends ServerMessage {
43 | type: "Linked";
44 | sessionId: string;
45 | onlineGuests: number;
46 | }
47 |
48 | export interface ServerMessageGetSessionConfigOK extends ServerMessage {
49 | type: "GetSessionConfigOK";
50 | id: IntNumber;
51 | sessionId: string;
52 | webhookId: string;
53 | webhookUrl: string;
54 | metadata: { [field: string]: string };
55 | }
56 |
57 | export interface ServerMessageSessionConfigUpdated extends ServerMessage {
58 | type: "SessionConfigUpdated";
59 | sessionId: string;
60 | webhookId: string;
61 | webhookUrl: string;
62 | metadata: { [field: string]: string };
63 | }
64 |
65 | export interface ServerMessagePublishEventOK extends ServerMessage {
66 | type: "PublishEventOK";
67 | id: IntNumber;
68 | sessionId: string;
69 | eventId: string;
70 | }
71 |
72 | export interface ServerMessageEvent extends ServerMessage {
73 | type: "Event";
74 | sessionId: string;
75 | eventId: string;
76 | event: string;
77 | data: string;
78 | }
79 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/connection/SessionConfig.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | export interface SessionConfig {
5 | webhookId: string;
6 | webhookUrl: string;
7 | metadata: { [key: string]: string | undefined };
8 | }
9 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/fixtures/provider.ts:
--------------------------------------------------------------------------------
1 | export const MOCK_ADDERESS = "0xFadAFCE89EA2221fa33005640Acf2C923312F2b9";
2 |
3 | export const MOCK_TX =
4 | "0xc21a1aaace40a8ee9dd3827ae5a85412a05755cc004469efaf3cfdd82c59a670";
5 |
6 | export const MOCK_SIGNED_TX =
7 | ":f87a1b8504a817c80082520894c589ac793af309db9690d819abc9aab37d169f6a8814d1120d7b1600008e0deadbeef0cafebabe01234567891ba03cd26b08b246f23f74fceb2c063021955e691cf7d45fba443a2e504a4700dba5a0337b1f8dbf21ef35adf6e2a867d9c7bc836d1b79c8ab40c670385a2d0abca88c";
8 |
9 | export const MOCK_TYPED_DATA = JSON.stringify({
10 | types: {
11 | EIP712Domain: [
12 | { name: "name", type: "string" },
13 | { name: "version", type: "string" },
14 | { name: "chainId", type: "uint256" },
15 | { name: "verifyingContract", type: "address" },
16 | { name: "salt", type: "bytes32" },
17 | ],
18 | Bid: [
19 | { name: "amount", type: "uint256" },
20 | { name: "bidder", type: "Identity" },
21 | ],
22 | Identity: [
23 | { name: "userId", type: "uint256" },
24 | { name: "wallet", type: "address" },
25 | ],
26 | },
27 | domain: {
28 | name: "Provider Test",
29 | version: "1",
30 | chainId: parseInt("1", 10),
31 | verifyingContract: MOCK_ADDERESS,
32 | salt: "0xf2d857f4a3edcb9b78b4d503bfe733db1e3f6cdc2b7971ee739626c97e86a558",
33 | },
34 | primaryType: "Bid",
35 | message: {
36 | amount: 100,
37 | bidder: {
38 | userId: 323,
39 | wallet: MOCK_ADDERESS,
40 | },
41 | },
42 | });
43 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/index.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | import { CoinbaseWalletSDK } from "./CoinbaseWalletSDK";
5 | import { CoinbaseWalletProvider } from "./provider/CoinbaseWalletProvider";
6 |
7 | export { CoinbaseWalletSDK } from "./CoinbaseWalletSDK";
8 | export { CoinbaseWalletProvider } from "./provider/CoinbaseWalletProvider";
9 | export default CoinbaseWalletSDK;
10 |
11 | declare global {
12 | interface Window {
13 | CoinbaseWalletSDK: typeof CoinbaseWalletSDK;
14 | CoinbaseWalletProvider: typeof CoinbaseWalletProvider;
15 | /**
16 | * For CoinbaseWalletSDK, window.ethereum is `CoinbaseWalletProvider`
17 | */
18 | ethereum?: unknown;
19 | coinbaseWalletExtension?: CoinbaseWalletProvider;
20 |
21 | /**
22 | * @deprecated Legacy API
23 | */
24 | WalletLink: typeof CoinbaseWalletSDK;
25 | /**
26 | * @deprecated Legacy API
27 | */
28 | WalletLinkProvider: typeof CoinbaseWalletProvider;
29 | /**
30 | * @deprecated Legacy API
31 | */
32 | walletLinkExtension?: CoinbaseWalletProvider;
33 | }
34 | }
35 |
36 | if (typeof window !== "undefined") {
37 | window.CoinbaseWalletSDK = CoinbaseWalletSDK;
38 | window.CoinbaseWalletProvider = CoinbaseWalletProvider;
39 |
40 | /**
41 | * @deprecated Use `window.CoinbaseWalletSDK`
42 | */
43 | window.WalletLink = CoinbaseWalletSDK;
44 | /**
45 | * @deprecated Use `window.CoinbaseWalletProvider`
46 | */
47 | window.WalletLinkProvider = CoinbaseWalletProvider;
48 | }
49 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/lib/ScopedLocalStorage.test.ts:
--------------------------------------------------------------------------------
1 | import { ScopedLocalStorage } from "./ScopedLocalStorage";
2 |
3 | describe("ScopedLocalStorage", () => {
4 | describe("public methods", () => {
5 | afterEach(() => localStorage.clear());
6 |
7 | const scopedLocalStorage = new ScopedLocalStorage("-testing");
8 | test("@setItem", () => {
9 | scopedLocalStorage.setItem("foo", "bar");
10 |
11 | expect(localStorage.getItem("-testing:foo")).toEqual("bar");
12 | expect(localStorage.length).toEqual(1);
13 | });
14 |
15 | test("@getItem", () => {
16 | scopedLocalStorage.setItem("foo", "bar");
17 | const getVal = scopedLocalStorage.getItem("foo");
18 |
19 | expect(getVal).toEqual("bar");
20 | });
21 |
22 | test("@removeItem", () => {
23 | scopedLocalStorage.removeItem("foo");
24 |
25 | expect(localStorage.length).toEqual(0);
26 | });
27 |
28 | test("@clear", () => {
29 | scopedLocalStorage.setItem("foo1", "bar1");
30 | scopedLocalStorage.setItem("foo2", "bar2");
31 | scopedLocalStorage.setItem("foo3", "bar3");
32 | expect(localStorage.length).toEqual(3);
33 |
34 | scopedLocalStorage.clear();
35 | expect(localStorage.length).toEqual(0);
36 | });
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/lib/ScopedLocalStorage.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | export class ScopedLocalStorage {
5 | constructor(private scope: string) {}
6 |
7 | public setItem(key: string, value: string): void {
8 | localStorage.setItem(this.scopedKey(key), value);
9 | }
10 |
11 | public getItem(key: string): string | null {
12 | return localStorage.getItem(this.scopedKey(key));
13 | }
14 |
15 | public removeItem(key: string): void {
16 | localStorage.removeItem(this.scopedKey(key));
17 | }
18 |
19 | public clear(): void {
20 | const prefix = this.scopedKey("");
21 | const keysToRemove: string[] = [];
22 | for (let i = 0; i < localStorage.length; i++) {
23 | const key = localStorage.key(i);
24 | if (typeof key === "string" && key.startsWith(prefix)) {
25 | keysToRemove.push(key);
26 | }
27 | }
28 | keysToRemove.forEach(key => localStorage.removeItem(key));
29 | }
30 |
31 | private scopedKey(key: string): string {
32 | return `${this.scope}:${key}`;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/lib/cssReset.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | import css from "./cssReset-css";
5 |
6 | export function injectCssReset(): void {
7 | const styleEl = document.createElement("style");
8 | styleEl.type = "text/css";
9 | styleEl.appendChild(document.createTextNode(css));
10 | document.documentElement.appendChild(styleEl);
11 | }
12 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/provider/JSONRPC.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | export enum JSONRPCMethod {
5 | // synchronous or asynchronous
6 | eth_accounts = "eth_accounts",
7 | eth_coinbase = "eth_coinbase",
8 | net_version = "net_version",
9 | eth_chainId = "eth_chainId",
10 | eth_uninstallFilter = "eth_uninstallFilter", // synchronous
11 |
12 | // asynchronous only
13 | eth_requestAccounts = "eth_requestAccounts",
14 | eth_sign = "eth_sign",
15 | eth_ecRecover = "eth_ecRecover",
16 | personal_sign = "personal_sign",
17 | personal_ecRecover = "personal_ecRecover",
18 | eth_signTransaction = "eth_signTransaction",
19 | eth_sendRawTransaction = "eth_sendRawTransaction",
20 | eth_sendTransaction = "eth_sendTransaction",
21 | eth_signTypedData_v1 = "eth_signTypedData_v1",
22 | eth_signTypedData_v2 = "eth_signTypedData_v2",
23 | eth_signTypedData_v3 = "eth_signTypedData_v3",
24 | eth_signTypedData_v4 = "eth_signTypedData_v4",
25 | eth_signTypedData = "eth_signTypedData",
26 | cbWallet_arbitrary = "walletlink_arbitrary", // compatibility
27 | wallet_addEthereumChain = "wallet_addEthereumChain",
28 | wallet_switchEthereumChain = "wallet_switchEthereumChain",
29 | wallet_watchAsset = "wallet_watchAsset",
30 |
31 | // asynchronous pub/sub
32 | eth_subscribe = "eth_subscribe",
33 | eth_unsubscribe = "eth_unsubscribe",
34 |
35 | // asynchronous filter methods
36 | eth_newFilter = "eth_newFilter",
37 | eth_newBlockFilter = "eth_newBlockFilter",
38 | eth_newPendingTransactionFilter = "eth_newPendingTransactionFilter",
39 | eth_getFilterChanges = "eth_getFilterChanges",
40 | eth_getFilterLogs = "eth_getFilterLogs",
41 | }
42 |
43 | export interface JSONRPCRequest {
44 | jsonrpc: "2.0";
45 | id: number;
46 | method: string;
47 | params: T;
48 | }
49 |
50 | export interface JSONRPCResponse {
51 | jsonrpc: "2.0";
52 | id: number;
53 | result?: T;
54 | error?: {
55 | code: number;
56 | message: string;
57 | data?: U;
58 | } | null;
59 | }
60 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/provider/SubscriptionManager.ts:
--------------------------------------------------------------------------------
1 | import SafeEventEmitter from "@metamask/safe-event-emitter";
2 | import {
3 | JsonRpcEngineEndCallback,
4 | JsonRpcEngineNextCallback,
5 | } from "json-rpc-engine";
6 |
7 | import { RequestArguments, Web3Provider } from "./Web3Provider";
8 |
9 | const PollingBlockTracker = require("eth-block-tracker");
10 | const createSubscriptionManager = require("eth-json-rpc-filters/subscriptionManager");
11 | const noop = () => {};
12 |
13 | export interface SubscriptionResult {
14 | result?: unknown;
15 | }
16 |
17 | export interface SubscriptionNotification {
18 | method: string;
19 | params: {
20 | subscription: string;
21 | result: unknown;
22 | };
23 | }
24 |
25 | export class SubscriptionManager {
26 | private readonly subscriptionMiddleware: SubscriptionMiddleware;
27 | readonly events: SafeEventEmitter;
28 |
29 | constructor(provider: Web3Provider) {
30 | const blockTracker = new PollingBlockTracker({
31 | provider,
32 | pollingInterval: 15 * 1000, // 15 sec
33 | setSkipCacheFlag: true,
34 | });
35 |
36 | const { events, middleware } = createSubscriptionManager({
37 | blockTracker,
38 | provider,
39 | });
40 |
41 | this.events = events;
42 | this.subscriptionMiddleware = middleware;
43 | }
44 |
45 | public async handleRequest(request: {
46 | method: string;
47 | params: any[];
48 | }): Promise {
49 | const result = {};
50 | await this.subscriptionMiddleware(request, result, noop, noop);
51 | return result;
52 | }
53 |
54 | public destroy() {
55 | this.subscriptionMiddleware.destroy();
56 | }
57 | }
58 |
59 | interface SubscriptionMiddleware {
60 | (
61 | req: RequestArguments,
62 | res: SubscriptionResult,
63 | next: JsonRpcEngineNextCallback,
64 | end: JsonRpcEngineEndCallback,
65 | ): Promise;
66 |
67 | destroy(): void;
68 | }
69 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/provider/WalletUIError.ts:
--------------------------------------------------------------------------------
1 | export class WalletUIError extends Error {
2 | private constructor(readonly message: string, readonly errorCode?: number) {
3 | super(message);
4 | }
5 |
6 | static UserRejectedRequest = new WalletUIError("User rejected request");
7 |
8 | static SwitchEthereumChainUnsupportedChainId = new WalletUIError(
9 | "Unsupported chainId",
10 | 4902,
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/provider/Web3Provider.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | import { Callback } from "../types";
5 | import { JSONRPCRequest, JSONRPCResponse } from "./JSONRPC";
6 |
7 | export interface Web3Provider {
8 | send(request: JSONRPCRequest): JSONRPCResponse;
9 | send(request: JSONRPCRequest[]): JSONRPCResponse[];
10 | send(request: JSONRPCRequest, callback: Callback): void;
11 | send(request: JSONRPCRequest[], callback: Callback): void;
12 | send(method: string, params?: any[] | any): Promise;
13 |
14 | sendAsync(request: JSONRPCRequest, callback: Callback): void;
15 | sendAsync(
16 | request: JSONRPCRequest[],
17 | callback: Callback,
18 | ): void;
19 |
20 | request(args: RequestArguments): Promise;
21 |
22 | host: string;
23 | connected: boolean;
24 | chainId: string;
25 | supportsSubscriptions(): boolean;
26 | disconnect(): boolean;
27 | }
28 |
29 | export interface RequestArguments {
30 | /** The RPC method to request. */
31 | method: string;
32 |
33 | /** The params of the RPC method, if any. */
34 | params?: any;
35 | }
36 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/relay/EthereumTransactionParams.ts:
--------------------------------------------------------------------------------
1 | import BN from "bn.js";
2 |
3 | import { AddressString, IntNumber } from "../types";
4 |
5 | export interface EthereumTransactionParams {
6 | fromAddress: AddressString;
7 | toAddress: AddressString | null;
8 | weiValue: BN;
9 | data: Buffer;
10 | nonce: IntNumber | null;
11 | gasPriceInWei: BN | null;
12 | maxFeePerGas: BN | null; // in wei
13 | maxPriorityFeePerGas: BN | null; // in wei
14 | gasLimit: BN | null;
15 | chainId: IntNumber;
16 | }
17 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/relay/RelayMessage.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | export enum RelayMessageType {
5 | SESSION_ID_REQUEST = "SESSION_ID_REQUEST",
6 | SESSION_ID_RESPONSE = "SESSION_ID_RESPONSE",
7 | LINKED = "LINKED",
8 | UNLINKED = "UNLINKED",
9 | WEB3_REQUEST = "WEB3_REQUEST",
10 | WEB3_REQUEST_CANCELED = "WEB3_REQUEST_CANCELED",
11 | WEB3_RESPONSE = "WEB3_RESPONSE",
12 | }
13 |
14 | export interface RelayMessage {
15 | type: T;
16 | }
17 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/relay/Session.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | import { sha256 } from "sha.js";
5 |
6 | import { ScopedLocalStorage } from "../lib/ScopedLocalStorage";
7 | import { randomBytesHex } from "../util";
8 |
9 | const STORAGE_KEY_SESSION_ID = "session:id";
10 | const STORAGE_KEY_SESSION_SECRET = "session:secret";
11 | const STORAGE_KEY_SESSION_LINKED = "session:linked";
12 |
13 | export class Session {
14 | private readonly _id: string;
15 | private readonly _secret: string;
16 | private readonly _key: string;
17 | private readonly _storage: ScopedLocalStorage;
18 | private _linked: boolean;
19 |
20 | constructor(
21 | storage: ScopedLocalStorage,
22 | id?: string,
23 | secret?: string,
24 | linked?: boolean,
25 | ) {
26 | this._storage = storage;
27 | this._id = id || randomBytesHex(16);
28 | this._secret = secret || randomBytesHex(32);
29 |
30 | this._key = new sha256()
31 | .update(`${this._id}, ${this._secret} WalletLink`) // ensure old sessions stay connected
32 | .digest("hex");
33 |
34 | this._linked = !!linked;
35 | }
36 |
37 | public static load(storage: ScopedLocalStorage): Session | null {
38 | const id = storage.getItem(STORAGE_KEY_SESSION_ID);
39 | const linked = storage.getItem(STORAGE_KEY_SESSION_LINKED);
40 | const secret = storage.getItem(STORAGE_KEY_SESSION_SECRET);
41 |
42 | if (id && secret) {
43 | return new Session(storage, id, secret, linked === "1");
44 | }
45 |
46 | return null;
47 | }
48 |
49 | /**
50 | * Takes in a session ID and returns the sha256 hash of it.
51 | * @param sessionId session ID
52 | */
53 | public static hash(sessionId: string): string {
54 | return new sha256().update(sessionId).digest("hex");
55 | }
56 |
57 | public get id(): string {
58 | return this._id;
59 | }
60 |
61 | public get secret(): string {
62 | return this._secret;
63 | }
64 |
65 | public get key(): string {
66 | return this._key;
67 | }
68 |
69 | public get linked(): boolean {
70 | return this._linked;
71 | }
72 |
73 | public set linked(val: boolean) {
74 | this._linked = val;
75 | this.persistLinked();
76 | }
77 |
78 | public save(): Session {
79 | this._storage.setItem(STORAGE_KEY_SESSION_ID, this._id);
80 | this._storage.setItem(STORAGE_KEY_SESSION_SECRET, this._secret);
81 | this.persistLinked();
82 | return this;
83 | }
84 |
85 | private persistLinked(): void {
86 | this._storage.setItem(STORAGE_KEY_SESSION_LINKED, this._linked ? "1" : "0");
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/relay/WalletSDKRelayEventManager.test.ts:
--------------------------------------------------------------------------------
1 | import { WalletSDKRelayEventManager } from "./WalletSDKRelayEventManager";
2 |
3 | describe("WalletSDKRelayEventManager", () => {
4 | test("@makeRequestId", () => {
5 | const sdkRelayEventManager = new WalletSDKRelayEventManager();
6 | expect(sdkRelayEventManager.makeRequestId()).toEqual(1);
7 | expect(sdkRelayEventManager.makeRequestId()).toEqual(2);
8 | expect(sdkRelayEventManager.makeRequestId()).toEqual(3);
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/relay/WalletSDKRelayEventManager.ts:
--------------------------------------------------------------------------------
1 | import { prepend0x } from "../util";
2 | import { Web3Response } from "./Web3Response";
3 |
4 | type ResponseCallback = (response: Web3Response) => void;
5 |
6 | export class WalletSDKRelayEventManager {
7 | _nextRequestId = 0;
8 | callbacks = new Map();
9 |
10 | public makeRequestId(): number {
11 | // max nextId == max int32 for compatibility with mobile
12 | this._nextRequestId = (this._nextRequestId + 1) % 0x7fffffff;
13 | const id = this._nextRequestId;
14 | const idStr = prepend0x(id.toString(16));
15 | // unlikely that this will ever be an issue, but just to be safe
16 | const callback = this.callbacks.get(idStr);
17 | if (callback) {
18 | this.callbacks.delete(idStr);
19 | }
20 | return id;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/relay/Web3Method.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | export enum Web3Method {
5 | requestEthereumAccounts = "requestEthereumAccounts",
6 | signEthereumMessage = "signEthereumMessage",
7 | signEthereumTransaction = "signEthereumTransaction",
8 | submitEthereumTransaction = "submitEthereumTransaction",
9 | ethereumAddressFromSignedMessage = "ethereumAddressFromSignedMessage",
10 | scanQRCode = "scanQRCode",
11 | generic = "generic",
12 | childRequestEthereumAccounts = "childRequestEthereumAccounts",
13 | addEthereumChain = "addEthereumChain",
14 | switchEthereumChain = "switchEthereumChain",
15 | makeEthereumJSONRPCRequest = "makeEthereumJSONRPCRequest",
16 | watchAsset = "watchAsset",
17 | selectProvider = "selectProvider",
18 | }
19 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/relay/Web3Request.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | import {
5 | AddressString,
6 | BigIntString,
7 | HexString,
8 | IntNumber,
9 | ProviderType,
10 | RegExpString,
11 | } from "../types";
12 | import { Web3Method } from "./Web3Method";
13 |
14 | interface BaseWeb3Request<
15 | Method extends Web3Method,
16 | Params extends object = Record,
17 | > {
18 | method: Method;
19 | params: Params;
20 | }
21 |
22 | export type RequestEthereumAccountsRequest = BaseWeb3Request<
23 | Web3Method.requestEthereumAccounts,
24 | {
25 | appName: string;
26 | appLogoUrl: string | null;
27 | }
28 | >;
29 |
30 | export type AddEthereumChainRequest = BaseWeb3Request<
31 | Web3Method.addEthereumChain,
32 | {
33 | chainId: string;
34 | blockExplorerUrls?: string[];
35 | chainName?: string;
36 | iconUrls?: string[];
37 | rpcUrls: string[];
38 | nativeCurrency?: {
39 | name: string;
40 | symbol: string;
41 | decimals: number;
42 | };
43 | }
44 | >;
45 |
46 | export type SwitchEthereumChainRequest = BaseWeb3Request<
47 | Web3Method.switchEthereumChain,
48 | {
49 | chainId: string;
50 | address?: string;
51 | }
52 | >;
53 |
54 | export type SignEthereumMessageRequest = BaseWeb3Request<
55 | Web3Method.signEthereumMessage,
56 | {
57 | message: HexString;
58 | address: AddressString;
59 | addPrefix: boolean;
60 | typedDataJson: string | null;
61 | }
62 | >;
63 |
64 | export type SignEthereumTransactionRequest = BaseWeb3Request<
65 | Web3Method.signEthereumTransaction,
66 | {
67 | fromAddress: AddressString;
68 | toAddress: AddressString | null;
69 | weiValue: BigIntString;
70 | data: HexString;
71 | nonce: IntNumber | null;
72 | gasPriceInWei: BigIntString | null;
73 | maxFeePerGas: BigIntString | null; // in wei
74 | maxPriorityFeePerGas: BigIntString | null; // in wei
75 | gasLimit: BigIntString | null;
76 | chainId: IntNumber;
77 | shouldSubmit: boolean;
78 | }
79 | >;
80 |
81 | export type SubmitEthereumTransactionRequest = BaseWeb3Request<
82 | Web3Method.submitEthereumTransaction,
83 | {
84 | signedTransaction: HexString;
85 | chainId: IntNumber;
86 | }
87 | >;
88 |
89 | export type EthereumAddressFromSignedMessageRequest = BaseWeb3Request<
90 | Web3Method.ethereumAddressFromSignedMessage,
91 | {
92 | message: HexString;
93 | signature: HexString;
94 | addPrefix: boolean;
95 | }
96 | >;
97 |
98 | export type ScanQRCodeRequest = BaseWeb3Request<
99 | Web3Method.scanQRCode,
100 | {
101 | regExp: RegExpString;
102 | }
103 | >;
104 |
105 | export type GenericRequest = BaseWeb3Request<
106 | Web3Method.generic,
107 | {
108 | action: string;
109 | data: object;
110 | }
111 | >;
112 |
113 | export type SelectProviderRequest = BaseWeb3Request<
114 | Web3Method.selectProvider,
115 | { providerOptions: ProviderType[] }
116 | >;
117 |
118 | export type MakeEthereumJSONRPCRequest = BaseWeb3Request<
119 | Web3Method.makeEthereumJSONRPCRequest,
120 | {
121 | rpcMethod: string;
122 | rpcParams: unknown[];
123 | chainId: string;
124 | }
125 | >;
126 |
127 | type WatchAssetRequestBaseParams = {
128 | type: string;
129 | options: {
130 | address: string;
131 | symbol?: string;
132 | decimals?: number;
133 | image?: string;
134 | };
135 | };
136 |
137 | export type WatchAssetRequest = BaseWeb3Request<
138 | Web3Method.watchAsset,
139 | WatchAssetRequestBaseParams & {
140 | chainId?: string;
141 | }
142 | >;
143 |
144 | export type Web3Request =
145 | | RequestEthereumAccountsRequest
146 | | SignEthereumMessageRequest
147 | | SignEthereumTransactionRequest
148 | | SubmitEthereumTransactionRequest
149 | | EthereumAddressFromSignedMessageRequest
150 | | ScanQRCodeRequest
151 | | GenericRequest
152 | | AddEthereumChainRequest
153 | | SwitchEthereumChainRequest
154 | | MakeEthereumJSONRPCRequest
155 | | WatchAssetRequest
156 | | SelectProviderRequest;
157 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/relay/Web3RequestCanceledMessage.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | import { RelayMessage, RelayMessageType } from "./RelayMessage";
5 |
6 | export interface Web3RequestCanceledMessage
7 | extends RelayMessage {
8 | id: string;
9 | }
10 |
11 | export function Web3RequestCanceledMessage(
12 | id: string,
13 | ): Web3RequestCanceledMessage {
14 | return { type: RelayMessageType.WEB3_REQUEST_CANCELED, id };
15 | }
16 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/relay/Web3RequestMessage.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | import { RelayMessage, RelayMessageType } from "./RelayMessage";
5 | import { Web3Request } from "./Web3Request";
6 |
7 | export interface Web3RequestMessage
8 | extends RelayMessage {
9 | id: string;
10 | request: Web3Request;
11 | }
12 |
13 | export function Web3RequestMessage(
14 | params: Omit,
15 | ): Web3RequestMessage {
16 | return { type: RelayMessageType.WEB3_REQUEST, ...params };
17 | }
18 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/relay/Web3ResponseMessage.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | import { RelayMessage, RelayMessageType } from "./RelayMessage";
5 | import { Web3Response } from "./Web3Response";
6 |
7 | export interface Web3ResponseMessage
8 | extends RelayMessage {
9 | type: RelayMessageType.WEB3_RESPONSE;
10 | id: string;
11 | response: Web3Response;
12 | }
13 |
14 | export function Web3ResponseMessage(
15 | params: Omit,
16 | ): Web3ResponseMessage {
17 | return { type: RelayMessageType.WEB3_RESPONSE, ...params };
18 | }
19 |
20 | export function isWeb3ResponseMessage(msg: any): msg is Web3ResponseMessage {
21 | return msg && msg.type === RelayMessageType.WEB3_RESPONSE;
22 | }
23 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/relay/aes256gcm.test.ts:
--------------------------------------------------------------------------------
1 | import { randomBytesHex } from "../util";
2 | import { decrypt, encrypt } from "./aes256gcm";
3 |
4 | const secret =
5 | "c356fe708ea7bbf7b1cc9ff9813c32772b6e0d16332da4c031ba9ea88be9b5ed";
6 |
7 | describe("aes256gcm", () => {
8 | test("encrypt/decrypt", async () => {
9 | const randSecret = randomBytesHex(32);
10 | const encrypted = await encrypt("plain text string", randSecret);
11 |
12 | expect(encrypted.length).toEqual(90);
13 |
14 | // decrypted output matches original input
15 | const decrypted = await decrypt(encrypted, randSecret);
16 |
17 | expect(decrypted).toBe("plain text string");
18 | });
19 |
20 | test("decrypt", async () => {
21 | const cipherText =
22 | "06593325a922a928913b5c6ea26f848c4545bcea4e26c4f5ee745316ff22b2780aeccc565730514b2820a94b03f5f89fe7542a35bbdd87a1d52a4352f49482781113db09266c668696778e0a94bc9f866f1e92e7262fd0bb811838284cc64cbc4552b33e9c6fb2582cea4f49471d6d46a16a5c8ac83ee8483ed4dc01f1fde3bfd7a2f173715e0a8d09dd4907483f096a845bff698831ea277c1ca4223d3f6073174cb35119d0a795c1a9cb4f32ee1dcc254d8931";
23 | const sampleDataResult = {
24 | type: "WEB3_RESPONSE",
25 | id: "791fe0ec3dc3de49",
26 | response: {
27 | method: "requestEthereumAccounts",
28 | result: ["0xdf0635793e91d4f8e7426dbd9ed08471186f428d"],
29 | },
30 | };
31 |
32 | const decrypted = await decrypt(cipherText, secret);
33 |
34 | expect(sampleDataResult).toEqual(JSON.parse(decrypted));
35 | });
36 |
37 | test("errors", async () => {
38 | await expect(encrypt("plain text string", "123456")).rejects.toThrowError(
39 | "secret must be 256 bits",
40 | );
41 |
42 | expect(() => decrypt("plain stext string", "123456")).toThrowError(
43 | "secret must be 256 bits",
44 | );
45 |
46 | await expect(decrypt("text", secret)).rejects.toThrow();
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/relay/aes256gcm.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | import { hexStringToUint8Array, uint8ArrayToHex } from "../util";
5 |
6 | /**
7 | *
8 | * @param plainText string to be encrypted
9 | * @param secret hex representation of 32-byte secret
10 | * returns hex string representation of bytes in the order: initialization vector (iv),
11 | * auth tag, encrypted plaintext. IV is 12 bytes. Auth tag is 16 bytes. Remaining bytes are the
12 | * encrypted plainText.
13 | */
14 | export async function encrypt(
15 | plainText: string,
16 | secret: string,
17 | ): Promise {
18 | if (secret.length !== 64) throw Error(`secret must be 256 bits`);
19 | const ivBytes = crypto.getRandomValues(new Uint8Array(12));
20 | const secretKey: CryptoKey = await crypto.subtle.importKey(
21 | "raw",
22 | hexStringToUint8Array(secret),
23 | { name: "aes-gcm" },
24 | false,
25 | ["encrypt", "decrypt"],
26 | );
27 |
28 | const enc = new TextEncoder();
29 |
30 | // Will return encrypted plainText with auth tag (ie MAC or checksum) appended at the end
31 | const encryptedResult: ArrayBuffer = await window.crypto.subtle.encrypt(
32 | {
33 | name: "AES-GCM",
34 | iv: ivBytes,
35 | },
36 | secretKey,
37 | enc.encode(plainText),
38 | );
39 |
40 | const tagLength = 16;
41 | const authTag: ArrayBuffer = encryptedResult.slice(
42 | encryptedResult.byteLength - tagLength,
43 | );
44 | const encryptedPlaintext = encryptedResult.slice(
45 | 0,
46 | encryptedResult.byteLength - tagLength,
47 | );
48 |
49 | const authTagBytes = new Uint8Array(authTag);
50 | const encryptedPlaintextBytes = new Uint8Array(encryptedPlaintext);
51 | const concatted = new Uint8Array([
52 | ...ivBytes,
53 | ...authTagBytes,
54 | ...encryptedPlaintextBytes,
55 | ]);
56 | return uint8ArrayToHex(concatted);
57 | }
58 |
59 | /**
60 | *
61 | * @param cipherText hex string representation of bytes in the order: initialization vector (iv),
62 | * auth tag, encrypted plaintext. IV is 12 bytes. Auth tag is 16 bytes.
63 | * @param secret hex string representation of 32-byte secret
64 | */
65 | export function decrypt(cipherText: string, secret: string): Promise {
66 | if (secret.length !== 64) throw Error(`secret must be 256 bits`);
67 | return new Promise((resolve, reject) => {
68 | void (async function () {
69 | const secretKey: CryptoKey = await crypto.subtle.importKey(
70 | "raw",
71 | hexStringToUint8Array(secret),
72 | { name: "aes-gcm" },
73 | false,
74 | ["encrypt", "decrypt"],
75 | );
76 |
77 | const encrypted: Uint8Array = hexStringToUint8Array(cipherText);
78 |
79 | const ivBytes = encrypted.slice(0, 12);
80 | const authTagBytes = encrypted.slice(12, 28);
81 | const encryptedPlaintextBytes = encrypted.slice(28);
82 | const concattedBytes = new Uint8Array([
83 | ...encryptedPlaintextBytes,
84 | ...authTagBytes,
85 | ]);
86 | const algo = {
87 | name: "AES-GCM",
88 | iv: new Uint8Array(ivBytes),
89 | };
90 | try {
91 | const decrypted = await window.crypto.subtle.decrypt(
92 | algo,
93 | secretKey,
94 | concattedBytes,
95 | );
96 | const decoder = new TextDecoder();
97 | resolve(decoder.decode(decrypted));
98 | } catch (err) {
99 | reject(err);
100 | }
101 | })();
102 | });
103 | }
104 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/relay/solana/SolanaWeb3Method.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | export enum SolanaWeb3Method {
5 | connect = "connect",
6 | signMessage = "signMessage",
7 | signTransaction = "signTransaction",
8 | signAllTransactions = "signAllTransactions",
9 | sendTransaction = "sendTransaction",
10 | }
11 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/relay/solana/SolanaWeb3Request.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | import { SendOptions, Signer, Transaction } from "@solana/web3.js";
5 |
6 | import { SolanaWeb3Method } from "./SolanaWeb3Method";
7 |
8 | interface BaseSolanaWeb3Request<
9 | Method extends SolanaWeb3Method,
10 | Params extends object = Record,
11 | > {
12 | method: Method;
13 | params: Params;
14 | }
15 |
16 | // this is for consistency with solana-labs/wallet-adapter
17 | interface SendTransactionOptions extends SendOptions {
18 | signers?: Signer[];
19 | }
20 |
21 | export type SolanaSignTransactionRequest = BaseSolanaWeb3Request<
22 | SolanaWeb3Method.signTransaction,
23 | {
24 | transactions: Transaction[];
25 | }
26 | >;
27 |
28 | export type SolanaSignAllTransactionsRequest = BaseSolanaWeb3Request<
29 | SolanaWeb3Method.signAllTransactions,
30 | {
31 | transactions: Transaction[];
32 | }
33 | >;
34 |
35 | export type SolanaSignMessageRequest = BaseSolanaWeb3Request<
36 | SolanaWeb3Method.signMessage,
37 | {
38 | address: string;
39 | message: string;
40 | }
41 | >;
42 |
43 | export type SolanaSendTransactionRequest = BaseSolanaWeb3Request<
44 | SolanaWeb3Method.sendTransaction,
45 | {
46 | transactions: Transaction[];
47 | options: SendTransactionOptions;
48 | }
49 | >;
50 |
51 | export type SolanaTransactionRequest =
52 | | SolanaSignTransactionRequest
53 | | SolanaSendTransactionRequest
54 | | SolanaSignAllTransactionsRequest;
55 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/relay/solana/SolanaWeb3Response.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | export enum SolanaWeb3Response {
5 | connectionSuccess = "solanaConnectionSuccess",
6 | connectionDeny = "solanaConnectionDeny",
7 | signMessageSuccess = "signSolanaMessageSuccess",
8 | signMessageDeny = "signSolanaMessageDeny",
9 | sendTransactionSuccess = "sendSolanaTransactionSuccess",
10 | sendTransactionDeny = "sendSolanaTransactionDeny",
11 | signTransactionSuccess = "signSolanaTransactionSuccess",
12 | signTransactionDeny = "signaSolanaTransactionDeny",
13 | signAllTransactionsSuccess = "signAllSolanaTransactionsSuccess",
14 | signAllTransactionsDeny = "signAllSolanaTransactionsDeny",
15 | parentDisconnected = "parentDisconnected",
16 | featureFlagOff = "featureFlagOff",
17 | web3RequestCanceled = "web3RequestCanceled",
18 | }
19 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/types.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018-2022 Coinbase, Inc.
2 | // Licensed under the Apache License, version 2.0
3 |
4 | interface Tag {
5 | __tag__: T;
6 | __realType__: RealType;
7 | }
8 |
9 | export type OpaqueType = U & Tag;
10 |
11 | export function OpaqueType>() {
12 | return (value: T extends Tag ? U : never): T => value as T;
13 | }
14 |
15 | export type HexString = OpaqueType<"HexString", string>;
16 | export const HexString = OpaqueType();
17 |
18 | export type AddressString = OpaqueType<"AddressString", string>;
19 | export const AddressString = OpaqueType();
20 |
21 | export type BigIntString = OpaqueType<"BigIntString", string>;
22 | export const BigIntString = OpaqueType();
23 |
24 | export type IntNumber = OpaqueType<"IntNumber", number>;
25 | export function IntNumber(num: number): IntNumber {
26 | return Math.floor(num) as IntNumber;
27 | }
28 |
29 | export type RegExpString = OpaqueType<"RegExpString", string>;
30 | export const RegExpString = OpaqueType();
31 |
32 | export type Callback = (err: Error | null, result: T | null) => void;
33 |
34 | export enum ProviderType {
35 | CoinbaseWallet = "CoinbaseWallet",
36 | MetaMask = "MetaMask",
37 | Unselected = "",
38 | }
39 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/vendor-js/eth-eip712-util/LICENSE:
--------------------------------------------------------------------------------
1 | ISC License (ISC)
2 | Copyright 2017-2020 Dan Finlay
3 |
4 | Permission to use, copy, modify, and/or distribute this software for any purpose
5 | with or without fee is hereby granted, provided that the above copyright notice
6 | and this permission notice appear in all copies.
7 |
8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
10 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
12 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
13 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
14 | THIS SOFTWARE.
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/vendor-js/eth-eip712-util/index.d.ts:
--------------------------------------------------------------------------------
1 | const module: {
2 | hashForSignTypedDataLegacy: (params: { data: any }) => Buffer
3 | hashForSignTypedData_v3: (params: { data: any }) => Buffer
4 | hashForSignTypedData_v4: (params: { data: any }) => Buffer
5 | TypedDataUtils: {
6 | sanitizeData: (data: any) => any,
7 | hashStruct: (primaryType: string, data: any, types: any, useV4: any) => Buffer
8 | }
9 | }
10 |
11 | export = module
12 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/vendor-js/eth-eip712-util/util.js:
--------------------------------------------------------------------------------
1 | // Extracted from https://github.com/ethereumjs/ethereumjs-util and stripped out irrelevant code
2 | // Original code licensed under the Mozilla Public License Version 2.0
3 |
4 | const createKeccakHash = require('keccak/js')
5 | const BN = require('bn.js')
6 |
7 | /**
8 | * Returns a buffer filled with 0s
9 | * @method zeros
10 | * @param {Number} bytes the number of bytes the buffer should be
11 | * @return {Buffer}
12 | */
13 | function zeros (bytes) {
14 | return Buffer.allocUnsafe(bytes).fill(0)
15 | }
16 |
17 | /**
18 | * Left Pads an `Array` or `Buffer` with leading zeros till it has `length` bytes.
19 | * Or it truncates the beginning if it exceeds.
20 | * @method setLength
21 | * @param {Buffer|Array} msg the value to pad
22 | * @param {Number} length the number of bytes the output should be
23 | * @param {Boolean} [right=false] whether to start padding form the left or right
24 | * @return {Buffer|Array}
25 | */
26 | function setLength (msg, length, right) {
27 | const buf = zeros(length)
28 | msg = toBuffer(msg)
29 | if (right) {
30 | if (msg.length < length) {
31 | msg.copy(buf)
32 | return buf
33 | }
34 | return msg.slice(0, length)
35 | } else {
36 | if (msg.length < length) {
37 | msg.copy(buf, length - msg.length)
38 | return buf
39 | }
40 | return msg.slice(-length)
41 | }
42 | }
43 |
44 | /**
45 | * Right Pads an `Array` or `Buffer` with leading zeros till it has `length` bytes.
46 | * Or it truncates the beginning if it exceeds.
47 | * @param {Buffer|Array} msg the value to pad
48 | * @param {Number} length the number of bytes the output should be
49 | * @return {Buffer|Array}
50 | */
51 | function setLengthRight (msg, length) {
52 | return setLength(msg, length, true)
53 | }
54 |
55 | /**
56 | * Attempts to turn a value into a `Buffer`. As input it supports `Buffer`, `String`, `Number`, null/undefined, `BN` and other objects with a `toArray()` method.
57 | * @param {*} v the value
58 | */
59 | function toBuffer (v) {
60 | if (!Buffer.isBuffer(v)) {
61 | if (Array.isArray(v)) {
62 | v = Buffer.from(v)
63 | } else if (typeof v === 'string') {
64 | if (isHexString(v)) {
65 | v = Buffer.from(padToEven(stripHexPrefix(v)), 'hex')
66 | } else {
67 | v = Buffer.from(v)
68 | }
69 | } else if (typeof v === 'number') {
70 | v = intToBuffer(v)
71 | } else if (v === null || v === undefined) {
72 | v = Buffer.allocUnsafe(0)
73 | } else if (BN.isBN(v)) {
74 | v = v.toArrayLike(Buffer)
75 | } else if (v.toArray) {
76 | // converts a BN to a Buffer
77 | v = Buffer.from(v.toArray())
78 | } else {
79 | throw new Error('invalid type')
80 | }
81 | }
82 | return v
83 | }
84 |
85 | /**
86 | * Converts a `Buffer` into a hex `String`
87 | * @param {Buffer} buf
88 | * @return {String}
89 | */
90 | function bufferToHex (buf) {
91 | buf = toBuffer(buf)
92 | return '0x' + buf.toString('hex')
93 | }
94 |
95 | /**
96 | * Creates Keccak hash of the input
97 | * @param {Buffer|Array|String|Number} a the input data
98 | * @param {Number} [bits=256] the Keccak width
99 | * @return {Buffer}
100 | */
101 | function keccak (a, bits) {
102 | a = toBuffer(a)
103 | if (!bits) bits = 256
104 |
105 | return createKeccakHash('keccak' + bits).update(a).digest()
106 | }
107 |
108 | function padToEven (str) {
109 | return str.length % 2 ? '0' + str : str
110 | }
111 |
112 | function isHexString (str) {
113 | return typeof str === 'string' && str.match(/^0x[0-9A-Fa-f]*$/)
114 | }
115 |
116 | function stripHexPrefix (str) {
117 | if (typeof str === 'string' && str.startsWith('0x')) {
118 | return str.slice(2)
119 | }
120 | return str
121 | }
122 |
123 | module.exports = {
124 | zeros,
125 | setLength,
126 | setLengthRight,
127 | isHexString,
128 | stripHexPrefix,
129 | toBuffer,
130 | bufferToHex,
131 | keccak
132 | }
133 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/vendor-js/qrcode-svg/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 papnkukn
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/packages/wallet-sdk/src/version.ts:
--------------------------------------------------------------------------------
1 | export const LIB_VERSION = "3.6.4";
2 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "module": "commonjs",
6 | "moduleResolution": "node",
7 | "outDir": "./build/npm/dist",
8 | "target": "es2017"
9 | },
10 | "include": ["src", "src/@types"],
11 | "exclude": ["**/*.test.*", "__tests__", "examples"]
12 | }
13 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowJs": false,
4 | "allowSyntheticDefaultImports": true,
5 | "esModuleInterop": true,
6 | "experimentalDecorators": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "jsx": "react",
9 | "jsxFactory": "h",
10 | "lib": ["dom", "dom.iterable", "es2016"],
11 | "module": "commonjs",
12 | "moduleResolution": "node",
13 | "noErrorTruncation": true,
14 | "noFallthroughCasesInSwitch": true,
15 | "noImplicitReturns": true,
16 | "noUnusedLocals": true,
17 | "noUnusedParameters": true,
18 | "resolveJsonModule": true,
19 | "skipLibCheck": true,
20 | "strict": true,
21 | "target": "es2016",
22 | "types": ["node", "jest"]
23 | },
24 | "include": ["src", "src/@types", "jest.setup.*", "__tests__", "**/*.test.*"]
25 | }
26 |
--------------------------------------------------------------------------------
/packages/wallet-sdk/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const webpack = require("webpack");
3 | const { env } = process;
4 |
5 | const tsConfigPath = (exports.tsConfigPath = path.join(
6 | __dirname,
7 | "tsconfig.json",
8 | ));
9 |
10 | module.exports = {
11 | target: "web",
12 | entry: [
13 | "core-js/stable",
14 | "regenerator-runtime/runtime",
15 | "whatwg-fetch",
16 | "./src/index.ts",
17 | ],
18 | // devtool: 'inline-source-map',
19 | mode: "production",
20 | module: {
21 | rules: [
22 | {
23 | test: /\.tsx?$/,
24 | use: {
25 | loader: "ts-loader",
26 | options: {
27 | configFile: tsConfigPath,
28 | },
29 | },
30 | exclude: /node_modules/,
31 | },
32 | ],
33 | },
34 | resolve: {
35 | fallback: {
36 | fs: false,
37 | stream: require.resolve("stream-browserify"),
38 | buffer: require.resolve("buffer/"),
39 | util: require.resolve("util/"),
40 | },
41 | extensions: [".ts", ".tsx", ".js"],
42 | plugins: [],
43 | symlinks: false,
44 | },
45 | output: {
46 | filename: "CoinbaseWalletSDK.js",
47 | path: path.resolve(__dirname, "build"),
48 | },
49 | performance: {
50 | hints: false,
51 | },
52 | plugins: [
53 | new webpack.DefinePlugin({
54 | "process.env": {
55 | NODE_ENV: JSON.stringify(env.NODE_ENV) || JSON.stringify("production"),
56 | LINK_API_URL: JSON.stringify(env.LINK_API_URL),
57 | SDK_VERSION: JSON.stringify(require("./package.json").version),
58 | },
59 | }),
60 | new webpack.ProvidePlugin({
61 | Buffer: ["buffer", "Buffer"],
62 | }),
63 | ],
64 | };
65 |
--------------------------------------------------------------------------------