├── .gitignore
├── LICENSE
├── README.md
├── docs
├── README.md
└── connectors
│ ├── README.md
│ ├── argentx.md
│ └── braavos.md
├── lerna.json
├── package.json
├── packages
├── abstract-connector
│ ├── .gitignore
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── declarations.ts
│ │ └── index.ts
│ └── tsconfig.json
├── argentx-connector
│ ├── .gitignore
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── declarations.ts
│ │ ├── index.ts
│ │ └── types.ts
│ └── tsconfig.json
├── braavos-connector
│ ├── .gitignore
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── declarations.ts
│ │ ├── index.ts
│ │ └── types.ts
│ └── tsconfig.json
├── core
│ ├── .gitignore
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── manager.ts
│ │ ├── normalizers.ts
│ │ ├── provider.tsx
│ │ └── types.ts
│ ├── test
│ │ ├── normalizers.test.ts
│ │ └── provider.test.tsx
│ └── tsconfig.json
└── types
│ ├── .gitignore
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── src
│ └── index.ts
│ └── tsconfig.json
├── tsconfig.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | .DS_Store
3 | node_modules
4 | .cache
5 | .rts2_cache_cjs
6 | .rts2_cache_esm
7 | .rts2_cache_umd
8 | .rts2_cache_system
9 | dist
10 | .env
11 | out
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 dhruvkelawala
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.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # web3-starknet-react 🚀
2 |
3 | _A simple, maximally extensible, dependency minimized framework for building Starknet dApps_
4 |
5 | _Provides easy access to Starknet Interface all over the React App. Inspired from_ [web3-react](https://github.com/NoahZinsmeister/web3-react/tree/v6)
6 |
7 |
8 |
9 | [](https://lerna.js.org/)
10 | [](https://github.com/prettier/prettier)
11 |
12 | | Packages | `@latest` version | Size | Description |
13 | | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
14 | | 🏝 **Core** | | | |
15 | | `@web3-starknet-react/core` | [](https://www.npmjs.com/package/@web3-starknet-react/core/v/latest) | [](https://bundlephobia.com/result?p=@web3-starknet-react/core@latest) | [React](https://reactjs.org/) hooks and context for accessing Starknet |
16 | | 🔌 **Connectors** | | | |
17 | | _Browser Extension/dApp Browser_ | | | |
18 | | `@web3-starknet-react/braavos-connector` | [](https://www.npmjs.com/package/@web3-starknet-react/braavos-connector/v/latest) | [](https://bundlephobia.com/result?p=@web3-starknet-react/braavos-connector@latest) | [Braavos Wallet](https://chrome.google.com/webstore/detail/braavos-wallet/jnlgamecbpmbajjfhmmmlhejkemejdma) connector |
19 | | `@web3-starknet-react/argentx-connector` | [](https://www.npmjs.com/package/@web3-starknet-react/argentx-connector/v/latest) | [](https://bundlephobia.com/result?p=@web3-starknet-react/argentx-connector@latest) | [ArgentX Wallet](https://github.com/argentlabs/argent-x) connector |
20 | | 🔫 **Low Level** | | | |
21 | | `@web3-starknet-react/abstract-connector` | [](https://www.npmjs.com/package/@web3-starknet-react/abstract-connector/v/latest) | [](https://bundlephobia.com/result?p=@web3-starknet-react/abstract-connector@latest) | Low Level implementation of connector |
22 | | `@web3-starknet-react/types` | [](https://www.npmjs.com/package/@web3-starknet-react/types/v/latest) | [](https://bundlephobia.com/result?p=@web3-starknet-react/types@latest) | Shared [TypeScript](https://www.typescriptlang.org/) Types |
23 |
24 |
25 |
26 | #
27 |
28 | ## ⭐️ [Documentation](docs)
29 |
30 | ## Projects using `web3-starknet-react`
31 |
32 | _Open a PR to add your starknet project to the list_
33 |
34 | - [JediSwap](https://app.testnet.jediswap.xyz/#/swap)
35 |
36 | ## Local Development
37 |
38 | - Clone repo \
39 | `git clone https://github.com/dhruvkelawala/web3-starknet-react`
40 |
41 | - Install top-level dependencies \
42 | `yarn`
43 |
44 | - Install sub-dependencies \
45 | `yarn bootstrap`
46 |
47 | - Build and watch for changes \
48 | `yarn start`
49 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # `web3-starknet-react` Documentation
2 |
3 | - [Overview](#overview)
4 | - [Install](#install)
5 | - [web3-starknet-react@core API Reference](#web3-starknet-reactcore-api-reference)
6 | - [StarknetReactProvider](#starknetreactprovider)
7 | - [Props](#props)
8 | - [Example](#example)
9 | - [useStarknetReact](#usestarknetreact)
10 | - [Arguments](#arguments)
11 | - [Example](#example-1)
12 | - [createStarknetReactRoot](#createstarknetreactroot)
13 | - [Arguments](#arguments-1)
14 | - [Example](#example-2)
15 | - [getStarknetReactContext](#getstarknetreactcontext)
16 | - [Arguments](#arguments-2)
17 | - [Example](#example-3)
18 | - [UnsupportedChainIdError](#unsupportedchainiderror)
19 | - [Example](#example-4)
20 | - [Understanding Error Bubbling](#understanding-error-bubbling)
21 |
22 | ## Overview
23 |
24 | At a high level, `web3-starknet-react` is a state machine which ensures that certain key pieces of Starknet related data (the user's current account, account address, chainId, etc. ) relevant to your dApp are kept-up-to-date. To help with this, `web3-starknet-react` uses React's [Context](https://reactjs.org/docs/context.html) API to efficiently store this data, and inject it wherever you need it in your react app.
25 |
26 | The data conforms to the following interface:
27 |
28 | ```typescript
29 | interface StarknetReactContextInterface {
30 | activate: (
31 | connector: AbstractConnector,
32 | onError?: (error: Error | unknown) => void,
33 | throwsError?: boolean
34 | ) => Promise
35 | setError: (error: Error) => void
36 | deactivate: () => void
37 |
38 | connector?: AbstractConnectorInterface
39 | library?: T
40 | chainId?: number
41 | account?: AccountInterface | null
42 | connectedAddress?: string | null
43 |
44 | active: boolean
45 | error?: Error
46 | }
47 | ```
48 |
49 | The documentation that follows is for `@web3-starknet-react/core`, the package responsible for managing this context. To understand where data itself comes from, head over to the [connectors/folder](connectors/)
50 |
51 | ## Install
52 |
53 | - Add `react@>=16.8` to your project \
54 | `yarn add react`
55 |
56 | - Then install `web3-starknet-react` \
57 | `yarn add @web3-starknet-react/core`
58 |
59 | ## `web3-starknet-react@core` API Reference
60 |
61 | ### StarknetReactProvider
62 |
63 | `web3-starknet-react` relies on the existence of a `StarknetReactProvider` at the root of your application (or more accurately, at the root of the subtree which you which you'd like to have Starknet functionality). It requires a single `getLibrary` prop which is responsible for instantiating `starknet.js` library.
64 |
65 | Note: `web3-react` library gives the flexibility of using `web3.js` or `ethers.js` library. However, on Starknet, there is only `starknet.js` library. The `getLibrary` prop on `web3-starknet-react`, therefore, only support `starknet.js` provider. If a new library is emerges in future, we will have flexibility to support it by just making very small changes.
66 |
67 | #### Props
68 |
69 | ```typescript
70 | // Use starknet.js provider
71 | getLibrary: (provider?: Provider, connector?: AbstractConnectorInterface) => any
72 | ```
73 |
74 | #### Example
75 |
76 | ```javascript
77 | import { StarknetReactProvider } from '@web3-starknet-react/core'
78 |
79 | // import starknet.js
80 | import { Provider } from 'starknet'
81 |
82 | function getLibrary(provider, connector) {
83 | return new Provider(provider)
84 | }
85 |
86 | function App() {
87 | return {/* <...> */}
88 | }
89 | ```
90 |
91 | ### useStarknetReact
92 |
93 | If you're using Hooks (😇), useStarknetReact will be your best friend. Call it from within any function component to access context variables, just like that. It accepts an optional key argument, if you're using multiple roots.
94 |
95 | #### Arguments
96 |
97 | ```typescript
98 | key?: string
99 | ```
100 |
101 | #### Example
102 |
103 | ```javascript
104 | import { useStarknetReact } from '@web3-starknet-react/core'
105 |
106 | function Component() {
107 | const starknetReact = useStarknetReact()
108 | // ...
109 | }
110 | ```
111 |
112 | ### createStarknetReactRoot
113 |
114 | In some cases, your dApp may want to maintain >1 active Starknet connections simultaneously.
115 |
116 | In cases like these, you'll likely want to create a second (or maybe even third, but probably not fourth) root, which will function exactly like another [StarknetReactProvider](#starknetreactprovider) (in fact, StarknetReactProvider uses createStarknetReactRoot under the hood). It requires a `key` argument, used to identify the root to [usStarknetReact](#usestarknetreact) (or [getStarknetReactContext](#getstarknetreactcontext)).
117 |
118 | #### Arguments
119 |
120 | ```typescript
121 | key: string
122 | ```
123 |
124 | #### Example
125 |
126 | ```javascript
127 | import { StarknetReactProvider, createStarknetReactRoot } from '@web3-starknet-react/core'
128 |
129 | // import starknet.js
130 | import { Provider } from 'starknet'
131 |
132 | function getLibrary(provider, connector) {
133 | return new Provider(provider)
134 | }
135 |
136 | const StarknetReactProviderReloaded = createStarknetReactRoot('anotherOne')
137 |
138 | function App() {
139 | return (
140 |
141 | {/* <...> */}
142 |
143 | )
144 | }
145 | ```
146 |
147 | ### getStarknetReactContext
148 |
149 | If you're not using Hooks (why?😳), getStarknetReactContext is your savior. It gives direct access to the context returned by [createContext](https://reactjs.org/docs/context.html#reactcreatecontext), which will unlock the use of [contextType](https://reactjs.org/docs/context.html#classcontexttype) in class components, the [Context.Consumer](https://reactjs.org/docs/context.html#contextconsumer) pattern, or whatever other render prop/HOC/etc. shenanigans your manager whose personal site still runs on PHP is making you write. It accepts an optional `key` argument to identify the root.
150 |
151 | #### Arguments
152 |
153 | ```typescript
154 | key?: string
155 | ```
156 |
157 | #### Example
158 |
159 | ```javascript
160 | import { getStarknetReactContext } from '@web3-starknet-react/core'
161 |
162 | const starknetReactContext = getStarknetReactContext()
163 |
164 | // ...
165 | ```
166 |
167 | ### UnsupportedChainIdError
168 |
169 | This is an error which can be used to inform users that they're connected to an unsupported network.
170 |
171 | #### Example
172 |
173 | ```javascript
174 | import { UnsupportedChainIdError } from '@web3-starknet-react/core'
175 | // ...
176 |
177 | function Component() {
178 | const { error } = useStarknetReact()
179 | const isUnsupportedChainIdError = error instanceof UnsupportedChainIdError
180 | // ...
181 | }
182 | ```
183 |
184 | ## Understanding Error Bubbling
185 |
186 | Errors that occur during the initial activation of a connector (i.e. inside activate), are are handled in 1 of 4 ways:
187 |
188 | 1. In the case where there's been 1 or more other updates to the `web3-starknet-react` context between when activate was called and when it resolved with the data required to complete the activation, errors are silently suppressed (in development mode, a warning will be logged to the console). This should really only happen in cases where activation takes a very long time and the user does something in the intervening time, such as activating another connector, deactivating the current connector, etc.
189 | 2. If `throwErrors` (the third argument to activate) is passed, errors will be thrown and should be handled in a .catch. No updates to the `web3-starknet-react` context will occur.
190 | 3. If `onError` (the second argument to activate) is passed, that function is called with the error. No updates to the `web3-starknet-react` context will occur.
191 | 4. Otherwise, the error will be set in the `web3-starknet-react` context (along with the connector).
192 |
193 | Errors that occur while a connector is set are handled in 1 of 2 ways:
194 |
195 | 1. If an `onError` function was passed, this function is called with the error. No updates to the `web3-starknet-react` context will occur.
196 | 2. Otherwise, the error will be set in the `web3-starknet-react` context.
197 |
198 | In all of these scenarios, note that calling setError will update the `web3-starknet-react` context. This can be called any time a connector is set, and it can be useful for e.g. manually triggering your app's handling of the `web3-starknet-react` error property.
199 |
200 | Note: if an error is ever set in the `web3-starknet-react` context, and a connector triggers an update, the manager will attempt to revalidate all properties as if activate was called again, to recover from the error state.
201 |
--------------------------------------------------------------------------------
/docs/connectors/README.md:
--------------------------------------------------------------------------------
1 | # `web3-starknet-react` Documentation - Connectors
2 |
3 | Connectors are standalone packages that interface with `web3-starknet-react` and manage connections to a single type of Starknet node or wallet. To give an example: it's quite common for dApp users to rely on browser extensions such as [ArgentX](https://github.com/argentlabs/argent-x/) or [Braavos](https://chrome.google.com/webstore/detail/braavos-wallet/jnlgamecbpmbajjfhmmmlhejkemejdma) to manage their account for them. So, `web3-starknet-react` defines a connector that's responsible for interfacing with browser extensions. The current list of first-party `web3-starknet-react` connectors can be found in the [top-level README.md](../../README.md).
4 |
5 | Currently, we support both Braavos & ArgentX connectors. As and when new wallets emerges on Starknet like Ledger and Walletconnect, I will add them. Feel free to create an issue, if you want some wallet added.
6 |
7 | One enormous benefit to `web3-starknet-react` is that it's incredibly easy to extend existing/create new connectors! They're very simple Javascript class objects which simply need to define a few functions to be fully compatible with the rest of `web3-starknet-react`.
8 |
9 | For more information on specific first-party connectors, see the individual files in this folder.
10 |
--------------------------------------------------------------------------------
/docs/connectors/argentx.md:
--------------------------------------------------------------------------------
1 | # `web3-react` Documentation - ArgentX
2 |
3 | - [Install](#install)
4 | - [Arguments](#arguments)
5 | - [Example](#example)
6 | - [Errors](#errors)
7 | - [NoStarknetProviderError](#nostarknetprovidererror)
8 | - [Example](#example-1)
9 | - [UserRejectedRequestError](#userrejectedrequesterror)
10 | - [Example](#example-2)
11 |
12 | ## Install
13 |
14 | `yarn add @web3-starknet-react/argentx-connector`
15 |
16 | ## Arguments
17 |
18 | ```typescript
19 | supportedChainIds?: number[]
20 | ```
21 |
22 | **NOTE**: Currently only Goerli (chainId: 5) is supported. As soon as ArgentX supports mainnet (chainId: 1), I will add support for that.
23 |
24 | ## Example
25 |
26 | ```javascript
27 | import { ArgentXConnector } from '@web3-starknet-react/argentx-connector'
28 |
29 | const argentx = new ArgentXConnector({ supportedChainIds: [5] })
30 | ```
31 |
32 | ## Errors
33 |
34 | ### NoEthereumProviderError
35 |
36 | #### Example
37 |
38 | ```javascript
39 | import { NoStarknetProviderError } from '@web3-starknet-react/argentx-connector'
40 |
41 | function Component() {
42 | const { error } = useStarknetReact()
43 | const isNoStarknetProviderError = error instanceof NoStarknetProviderError
44 | // ...
45 | }
46 | ```
47 |
--------------------------------------------------------------------------------
/docs/connectors/braavos.md:
--------------------------------------------------------------------------------
1 | # `web3-react` Documentation - Braavos
2 |
3 | - [Install](#install)
4 | - [Arguments](#arguments)
5 | - [Example](#example)
6 | - [Errors](#errors)
7 | - [NoStarknetProviderError](#nostarknetprovidererror)
8 | - [Example](#example-1)
9 | - [UserRejectedRequestError](#userrejectedrequesterror)
10 | - [Example](#example-2)
11 |
12 | ## Install
13 |
14 | `yarn add @web3-starknet-react/braavos-connector`
15 |
16 | ## Arguments
17 |
18 | ```typescript
19 | supportedChainIds?: number[]
20 | ```
21 |
22 | ## Example
23 |
24 | ```javascript
25 | import { BraavosWallet } from '@web3-starknet-react/braavos-connector'
26 |
27 | const wallet = new BraavosWallet({ supportedChainIds: [5] })
28 | ```
29 |
30 | ## Errors
31 |
32 | ### NoEthereumProviderError
33 |
34 | #### Example
35 |
36 | ```javascript
37 | import { NoStarknetProviderError } from '@web3-starknet-react/braavos-connector'
38 |
39 | function Component() {
40 | const { error } = useStarknetReact()
41 | const isNoStarknetProviderError = error instanceof NoStarknetProviderError
42 | // ...
43 | }
44 | ```
45 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages": [
3 | "packages/*"
4 | ],
5 | "version": "2.1.2",
6 | "npmClient": "yarn",
7 | "useWorkspaces": true
8 | }
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "web3-starknet-react",
4 | "description": "A Starknet React Provider and Connectors inspired by web3-react library",
5 | "keywords": [
6 | "react",
7 | "react-hooks",
8 | "hooks",
9 | "ethereum",
10 | "javascript",
11 | "typescript",
12 | "web3",
13 | "starknet",
14 | "frontend",
15 | "dapp",
16 | "argentx",
17 | "braavos"
18 | ],
19 | "author": "Dhruv Kelawala ",
20 | "repository": {
21 | "type": "git",
22 | "url": "git://github.com/dhruvkelawala/web3-starknet-react.git"
23 | },
24 | "scripts": {
25 | "bootstrap": "lerna bootstrap",
26 | "publish:lerna": "lerna bootstrap && yarn build && lerna publish",
27 | "clean": "lerna clean --yes && lerna exec -- rimraf yarn.lock dist/ .rts2_cache_{cjs,esm}/",
28 | "build": "lerna run build",
29 | "start": "lerna run --parallel --no-bail start -- --noClean",
30 | "lint": "lerna run lint --parallel",
31 | "test": "lerna run test --parallel"
32 | },
33 | "workspaces": [
34 | "packages/*"
35 | ],
36 | "devDependencies": {
37 | "@types/jest": "^24.0.21",
38 | "@types/react": "^16.9.11",
39 | "@types/react-dom": "^16.9.4",
40 | "lerna": "^3.19.0",
41 | "react": ">=16.8",
42 | "react-dom": ">=16.8",
43 | "rimraf": "^3.0.1",
44 | "tsdx": "^0.11.0"
45 | },
46 | "dependencies": {
47 | "starknet": "^3.12.3",
48 | "tiny-invariant": "^1.2.0",
49 | "tiny-warning": "^1.0.3"
50 | },
51 | "license": "GPL-3.0-or-later",
52 | "prettier": {
53 | "semi": false,
54 | "singleQuote": true,
55 | "printWidth": 120
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/packages/abstract-connector/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | .DS_Store
3 | node_modules
4 | .cache
5 | .rts2_cache_cjs
6 | .rts2_cache_esm
7 | .rts2_cache_umd
8 | .rts2_cache_system
9 | dist
10 |
--------------------------------------------------------------------------------
/packages/abstract-connector/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 dhruvkelawala
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.
22 |
--------------------------------------------------------------------------------
/packages/abstract-connector/README.md:
--------------------------------------------------------------------------------
1 | # @web3-starknet-react/abstract-connector
2 |
3 | Please visit the [parent `web3-starknet-react` repository](https://github.com/dhruvkelawala/web3-starknet-react/) for documentation and details on this package.
4 |
--------------------------------------------------------------------------------
/packages/abstract-connector/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@web3-starknet-react/abstract-connector",
3 | "version": "2.1.0",
4 | "publishConfig": {
5 | "access": "public"
6 | },
7 | "author": "Dhruv Kelawala ",
8 | "main": "dist/index.js",
9 | "module": "dist/abstract-connector.esm.js",
10 | "typings": "dist/index.d.ts",
11 | "files": [
12 | "dist"
13 | ],
14 | "scripts": {
15 | "start": "tsdx watch",
16 | "build": "tsdx build",
17 | "test": "tsdx test --env=jsdom",
18 | "lint": "tsdx lint"
19 | },
20 | "peerDependencies": {
21 | "react": ">=16"
22 | },
23 | "husky": {
24 | "hooks": {
25 | "pre-commit": "tsdx lint"
26 | }
27 | },
28 | "prettier": {
29 | "printWidth": 80,
30 | "semi": true,
31 | "singleQuote": true,
32 | "trailingComma": "es5"
33 | },
34 | "dependencies": {
35 | "@web3-starknet-react/types": "^2.1.0"
36 | },
37 | "gitHead": "b893495a21b7d761d895aac9e89666875c315e7b"
38 | }
39 |
--------------------------------------------------------------------------------
/packages/abstract-connector/src/declarations.ts:
--------------------------------------------------------------------------------
1 | declare const __DEV__: boolean;
2 |
--------------------------------------------------------------------------------
/packages/abstract-connector/src/index.ts:
--------------------------------------------------------------------------------
1 | import { EventEmitter } from 'events';
2 | import {
3 | AbstractConnectorArguments,
4 | ConnectorEvent,
5 | ConnectorUpdate,
6 | } from '@web3-starknet-react/types';
7 | import { AccountInterface, Provider } from 'starknet';
8 |
9 | export abstract class AbstractConnector extends EventEmitter {
10 | public readonly supportedChainIds?: number[];
11 |
12 | constructor({ supportedChainIds }: AbstractConnectorArguments = {}) {
13 | super();
14 |
15 | this.supportedChainIds = supportedChainIds;
16 | }
17 |
18 | public abstract activate(): Promise;
19 | public abstract getProvider(): Promise;
20 | public abstract getSigner(): AccountInterface | undefined;
21 | public abstract getChainId(): Promise;
22 | public abstract getAccount(): AccountInterface | undefined;
23 | public abstract getConnectedAddress(): string | null;
24 | public abstract deactivate(): void;
25 |
26 | protected emitUpdate(update: ConnectorUpdate) {
27 | if (__DEV__) {
28 | console.log(`Emitting '${ConnectorEvent.Update}' with payload `, update);
29 | }
30 |
31 | this.emit(ConnectorEvent.Update, update);
32 | }
33 |
34 | protected emitError(error: Error) {
35 | if (__DEV__) {
36 | console.log(`Emitting '${ConnectorEvent.Error}' with payload`, error);
37 | }
38 |
39 | this.emit(ConnectorEvent.Error, error);
40 | }
41 |
42 | protected emitDeactivate(): void {
43 | if (__DEV__) {
44 | console.log(`Emitting '${ConnectorEvent.Deactivate}'`);
45 | }
46 | this.emit(ConnectorEvent.Deactivate);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/packages/abstract-connector/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": ["src"],
4 | "compilerOptions": {
5 | "rootDir": "./",
6 | "baseUrl": "./",
7 | "paths": {
8 | "*": ["src/*", "node_modules/*"]
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/argentx-connector/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | .DS_Store
3 | node_modules
4 | .cache
5 | .rts2_cache_cjs
6 | .rts2_cache_esm
7 | .rts2_cache_umd
8 | .rts2_cache_system
9 | dist
10 |
--------------------------------------------------------------------------------
/packages/argentx-connector/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 dhruvkelawala
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/argentx-connector/README.md:
--------------------------------------------------------------------------------
1 | # @web3-starknet-react/argentx-connector
2 |
3 | Please visit the [parent `web3-starknet-react` repository](https://github.com/dhruvkelawala/web3-starknet-react/) for documentation and details on this package.
4 |
--------------------------------------------------------------------------------
/packages/argentx-connector/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@web3-starknet-react/argentx-connector",
3 | "publishConfig": {
4 | "access": "public"
5 | },
6 | "version": "2.1.2",
7 | "author": "Dhruv Kelawala ",
8 | "main": "dist/index.js",
9 | "module": "dist/argentx-connector.esm.js",
10 | "typings": "dist/index.d.ts",
11 | "files": [
12 | "dist"
13 | ],
14 | "scripts": {
15 | "start": "tsdx watch",
16 | "build": "tsdx build",
17 | "test": "tsdx test --env=jsdom",
18 | "lint": "tsdx lint"
19 | },
20 | "peerDependencies": {
21 | "react": ">=16"
22 | },
23 | "husky": {
24 | "hooks": {
25 | "pre-commit": "tsdx lint"
26 | }
27 | },
28 | "prettier": {
29 | "printWidth": 80,
30 | "semi": true,
31 | "singleQuote": true,
32 | "trailingComma": "es5"
33 | },
34 | "dependencies": {
35 | "@web3-starknet-react/abstract-connector": "^2.1.0",
36 | "@web3-starknet-react/types": "^2.1.0"
37 | },
38 | "gitHead": "b893495a21b7d761d895aac9e89666875c315e7b"
39 | }
40 |
--------------------------------------------------------------------------------
/packages/argentx-connector/src/declarations.ts:
--------------------------------------------------------------------------------
1 | declare const __DEV__: boolean;
2 |
--------------------------------------------------------------------------------
/packages/argentx-connector/src/index.ts:
--------------------------------------------------------------------------------
1 | import { AbstractConnector } from '@web3-starknet-react/abstract-connector';
2 | import {
3 | AbstractConnectorArguments,
4 | ConnectorUpdate,
5 | } from '@web3-starknet-react/types';
6 | import { AccountInterface } from 'starknet';
7 |
8 | export class NoStarknetProviderError extends Error {
9 | public constructor() {
10 | super();
11 | this.name = this.constructor.name;
12 | this.message = 'No Starknet Provider was found on window object';
13 | }
14 | }
15 |
16 | export class ArgentXConnector extends AbstractConnector {
17 | constructor(kwargs: AbstractConnectorArguments) {
18 | super(kwargs);
19 |
20 | this.handleAccountsChanged = this.handleAccountsChanged.bind(this);
21 | }
22 |
23 | public async activate(): Promise {
24 | if (!window.starknet) {
25 | throw new NoStarknetProviderError();
26 | }
27 | if (window.starknet?.on) {
28 | window.starknet.on('accountsChanged', this.handleAccountsChanged);
29 | }
30 |
31 | // const { ...provider } = this.starknet.signer;
32 |
33 | let account: AccountInterface | undefined, connectedAddress;
34 |
35 | // const isPreAuthorized = await window.starknet.isPreauthorized();
36 |
37 | connectedAddress = window.starknet.selectedAddress;
38 | account = window.starknet.account;
39 |
40 | if (!account) {
41 | [connectedAddress] = await window.starknet.enable();
42 | account = window.starknet.account;
43 | }
44 |
45 | return {
46 | provider: window.starknet.provider,
47 | chainId: 5,
48 | ...(account ? { account } : {}),
49 | ...(connectedAddress ? { connectedAddress } : {}),
50 | };
51 | }
52 |
53 | private handleAccountsChanged(accountAddresses: string[]) {
54 | if (__DEV__) {
55 | console.log(
56 | "Handling 'accountsChanged' event with payload",
57 | accountAddresses
58 | );
59 | }
60 |
61 | if (accountAddresses.length === 0) {
62 | this.emitDeactivate();
63 | } else {
64 | this.emitUpdate({ connectedAddress: accountAddresses[0] });
65 | }
66 | }
67 |
68 | public async getProvider(): Promise {
69 | return window.starknet?.provider;
70 | }
71 |
72 | /**
73 | * @deprecated Use getAccount()
74 | * @returns AccountInterface
75 | */
76 | public getSigner(): AccountInterface | undefined {
77 | if (!window.starknet) {
78 | throw new NoStarknetProviderError();
79 | }
80 |
81 | return window.starknet.account;
82 | }
83 |
84 | public async getChainId(): Promise {
85 | if (!window.starknet) {
86 | throw new NoStarknetProviderError();
87 | }
88 |
89 | // Temporary
90 | return 5;
91 | }
92 |
93 | public getAccount(): AccountInterface | undefined {
94 | if (!window.starknet) {
95 | throw new NoStarknetProviderError();
96 | }
97 |
98 | return window.starknet.account;
99 | }
100 |
101 | public getConnectedAddress(): string | null {
102 | if (!window.starknet) {
103 | throw new NoStarknetProviderError();
104 | }
105 |
106 | const address = window.starknet.selectedAddress;
107 |
108 | return address ? address : null;
109 | }
110 |
111 | public deactivate(): void {
112 | if (window.starknet) {
113 | this.handleAccountsChanged([]);
114 | }
115 | }
116 |
117 | public async isAuthorized(): Promise {
118 | let account;
119 | if (!window.starknet) {
120 | return false;
121 | } else if (window.starknet.selectedAddress) {
122 | account = window.starknet.selectedAddress;
123 | } else {
124 | [account] = await window.starknet.enable();
125 | }
126 |
127 | return !!account;
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/packages/argentx-connector/src/types.ts:
--------------------------------------------------------------------------------
1 | export type EventHandler = (accounts: string[]) => void;
2 |
3 | interface WatchAssetParameters {
4 | type: 'ERC20'; // The asset's interface, e.g. 'ERC20'
5 | options: {
6 | address: string; // The hexadecimal StarkNet address of the token contract
7 | symbol?: string; // A ticker symbol or shorthand, up to 5 alphanumerical characters
8 | decimals?: number; // The number of asset decimals
9 | image?: string; // A string url of the token logo
10 | name?: string; // The name of the token - not in spec
11 | };
12 | }
13 |
14 | export type RpcMessage = {
15 | type: 'wallet_watchAsset';
16 | params: WatchAssetParameters;
17 | };
18 |
19 | // eslint-disable-next-line @typescript-eslint/interface-name-prefix
20 | // interface IStarknetWindowObject {
21 | // enable: () => Promise;
22 | // on: (method: 'accountsChanged', handleEvent: EventHandler) => void;
23 | // off: (event: 'accountsChanged', handleEvent: EventHandler) => void;
24 | // signer?: import('@jediswap/starknet').SignerInterface;
25 | // provider: import('@jediswap/starknet').Provider;
26 | // selectedAddress?: string;
27 | // request: (call: RpcMessage) => Promise;
28 | // }
29 |
30 | interface IStarknetWindowObject {
31 | request: (call: RpcMessage) => Promise;
32 | enable: (options?: { showModal?: boolean }) => Promise;
33 | isPreauthorized: () => Promise;
34 | on: (event: 'accountsChanged', handleEvent: EventHandler) => void;
35 | off: (event: 'accountsChanged', handleEvent: EventHandler) => void;
36 | account?: import('starknet').AccountInterface;
37 | provider: import('starknet').Provider;
38 | selectedAddress?: string;
39 | version: string;
40 | }
41 |
42 | interface ConnectedStarknetWindowObject extends IStarknetWindowObject {
43 | isConnected: true;
44 | account: import('starknet').AccountInterface;
45 | selectedAddress: string;
46 | }
47 |
48 | interface DisconnectedStarknetWindowObject extends IStarknetWindowObject {
49 | isConnected: false;
50 | }
51 |
52 | export type StarknetWindowObject =
53 | | ConnectedStarknetWindowObject
54 | | DisconnectedStarknetWindowObject;
55 |
56 | declare global {
57 | interface Window {
58 | starknet?: StarknetWindowObject;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/packages/argentx-connector/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": ["src"],
4 | "compilerOptions": {
5 | "rootDir": "./",
6 | "baseUrl": "./",
7 | "paths": {
8 | "*": ["src/*", "node_modules/*"]
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/braavos-connector/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | .DS_Store
3 | node_modules
4 | .cache
5 | .rts2_cache_cjs
6 | .rts2_cache_esm
7 | .rts2_cache_umd
8 | .rts2_cache_system
9 | dist
10 | .idea
11 |
--------------------------------------------------------------------------------
/packages/braavos-connector/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 | -----------
3 |
4 | Copyright (c) 2022 avimak
5 | Permission is hereby granted, free of charge, to any person
6 | obtaining a copy of this software and associated documentation
7 | files (the "Software"), to deal in the Software without
8 | restriction, including without limitation the rights to use,
9 | copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the
11 | Software is furnished to do so, subject to the following
12 | conditions:
13 |
14 | The above copyright notice and this permission notice shall be
15 | included in all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 | OTHER DEALINGS IN THE SOFTWARE.
25 |
--------------------------------------------------------------------------------
/packages/braavos-connector/README.md:
--------------------------------------------------------------------------------
1 | # @web3-starknet-react/braavos-connector
2 |
3 | Please visit the [parent `web3-starknet-react` repository](https://github.com/dhruvkelawala/web3-starknet-react/) for documentation and details on this package.
4 |
--------------------------------------------------------------------------------
/packages/braavos-connector/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@web3-starknet-react/braavos-connector",
3 | "publishConfig": {
4 | "access": "public"
5 | },
6 | "version": "2.1.1",
7 | "author": "avimak@braavos.app",
8 | "main": "dist/index.js",
9 | "module": "dist/braavos-connector.esm.js",
10 | "typings": "dist/index.d.ts",
11 | "files": [
12 | "dist"
13 | ],
14 | "scripts": {
15 | "start": "tsdx watch",
16 | "build": "tsdx build",
17 | "test": "tsdx test --env=jsdom",
18 | "lint": "tsdx lint"
19 | },
20 | "peerDependencies": {
21 | "react": ">=16"
22 | },
23 | "husky": {
24 | "hooks": {
25 | "pre-commit": "tsdx lint"
26 | }
27 | },
28 | "prettier": {
29 | "printWidth": 80,
30 | "semi": true,
31 | "singleQuote": true,
32 | "trailingComma": "es5"
33 | },
34 | "dependencies": {
35 | "@web3-starknet-react/abstract-connector": "^2.1.0",
36 | "@web3-starknet-react/types": "^2.1.0"
37 | },
38 | "devDependencies": {
39 | "get-starknet": "^1.0.0"
40 | },
41 | "gitHead": "b893495a21b7d761d895aac9e89666875c315e7b"
42 | }
43 |
--------------------------------------------------------------------------------
/packages/braavos-connector/src/declarations.ts:
--------------------------------------------------------------------------------
1 | declare const __DEV__: boolean;
2 |
--------------------------------------------------------------------------------
/packages/braavos-connector/src/index.ts:
--------------------------------------------------------------------------------
1 | import { AbstractConnector } from '@web3-starknet-react/abstract-connector';
2 | import { ConnectorUpdate } from '@web3-starknet-react/types';
3 | import { AccountInterface } from 'starknet';
4 | import { IStarknetWindowObject } from 'get-starknet';
5 |
6 | export class NoStarknetProviderError extends Error {
7 | public constructor() {
8 | super();
9 | this.name = this.constructor.name;
10 | this.message = 'No Starknet Provider was found on window object';
11 | }
12 | }
13 |
14 | export class BraavosConnector extends AbstractConnector {
15 | public async activate(): Promise {
16 | const wallet = this.getBraavosWallet();
17 |
18 | if (!wallet) {
19 | throw new NoStarknetProviderError();
20 | }
21 |
22 | wallet.on('accountsChanged', this.handleAccountsChanged);
23 |
24 | let account: AccountInterface | undefined = wallet.account;
25 | let connectedAddress: string | undefined = wallet.selectedAddress;
26 |
27 | if (!connectedAddress) {
28 | [connectedAddress] = (await wallet.enable()) ?? [];
29 | account = wallet.account;
30 | }
31 |
32 | return {
33 | provider: wallet.provider,
34 | chainId: 5,
35 | ...(account ? { account } : {}),
36 | ...(connectedAddress ? { connectedAddress } : {}),
37 | };
38 | }
39 |
40 | private handleAccountsChanged = (accountAddresses: string[]) => {
41 | if (__DEV__) {
42 | console.log(
43 | "Handling 'accountsChanged' event with payload",
44 | accountAddresses
45 | );
46 | }
47 |
48 | if (accountAddresses.length === 0) {
49 | this.emitDeactivate();
50 | } else {
51 | this.emitUpdate({ connectedAddress: accountAddresses[0] });
52 | }
53 | };
54 |
55 | public async getProvider(): Promise {
56 | return this.getBraavosWallet()?.provider;
57 | }
58 |
59 | /**
60 | * @deprecated Use getAccount()
61 | * @returns AccountInterface
62 | */
63 | public getSigner(): AccountInterface | undefined {
64 | return this.getAccount();
65 | }
66 |
67 | public async getChainId(): Promise {
68 | const wallet = this.getBraavosWallet();
69 | if (!wallet) {
70 | throw new NoStarknetProviderError();
71 | }
72 |
73 | // Temporary
74 | return 5;
75 | }
76 |
77 | public getAccount(): AccountInterface | undefined {
78 | const wallet = this.getBraavosWallet();
79 | if (!wallet) {
80 | throw new NoStarknetProviderError();
81 | }
82 |
83 | return wallet.account;
84 | }
85 |
86 | public getConnectedAddress(): string | null {
87 | const wallet = this.getBraavosWallet();
88 | if (!wallet) {
89 | throw new NoStarknetProviderError();
90 | }
91 |
92 | return wallet.selectedAddress || null;
93 | }
94 |
95 | public deactivate(): void {
96 | if (this.getBraavosWallet()) {
97 | this.handleAccountsChanged([]);
98 | }
99 | }
100 |
101 | public async isAuthorized(): Promise {
102 | const wallet = this.getBraavosWallet();
103 | return wallet?.isPreauthorized() ?? false;
104 | }
105 |
106 | private getBraavosWallet = (): IStarknetWindowObject | undefined =>
107 | [window.starknet, window.starknet_braavos].find(
108 | obj => obj?.id === 'braavos'
109 | );
110 | }
111 |
--------------------------------------------------------------------------------
/packages/braavos-connector/src/types.ts:
--------------------------------------------------------------------------------
1 | import { IStarknetWindowObject } from 'get-starknet';
2 |
3 | declare global {
4 | interface Window {
5 | starknet?: IStarknetWindowObject;
6 | starknet_braavos?: IStarknetWindowObject;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/braavos-connector/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": ["src"],
4 | "compilerOptions": {
5 | "rootDir": "./",
6 | "baseUrl": "./",
7 | "paths": {
8 | "*": ["src/*", "node_modules/*"]
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/core/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | .DS_Store
3 | node_modules
4 | .cache
5 | .rts2_cache_cjs
6 | .rts2_cache_esm
7 | .rts2_cache_umd
8 | .rts2_cache_system
9 | dist
10 |
--------------------------------------------------------------------------------
/packages/core/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 dhruvkelawala
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.
22 |
--------------------------------------------------------------------------------
/packages/core/README.md:
--------------------------------------------------------------------------------
1 | # @web3-starknet-react/core
2 |
3 | Please visit the [parent `web3-starknet-react` repository](https://github.com/dhruvkelawala/web3-starknet-react/) for documentation and details on this package.
4 |
--------------------------------------------------------------------------------
/packages/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@web3-starknet-react/core",
3 | "publishConfig": {
4 | "access": "public"
5 | },
6 | "version": "2.1.0",
7 | "author": "Dhruv Kelawala ",
8 | "main": "dist/index.js",
9 | "module": "dist/core.esm.js",
10 | "typings": "dist/index.d.ts",
11 | "files": [
12 | "dist"
13 | ],
14 | "scripts": {
15 | "start": "tsdx watch",
16 | "build": "tsdx build",
17 | "test": "tsdx test --env=jsdom",
18 | "lint": "tsdx lint"
19 | },
20 | "peerDependencies": {
21 | "react": ">=16"
22 | },
23 | "husky": {
24 | "hooks": {
25 | "pre-commit": "tsdx lint"
26 | }
27 | },
28 | "prettier": {
29 | "printWidth": 80,
30 | "semi": true,
31 | "singleQuote": true,
32 | "trailingComma": "es5"
33 | },
34 | "dependencies": {
35 | "@web3-starknet-react/abstract-connector": "^2.1.0",
36 | "@web3-starknet-react/types": "^2.1.0"
37 | },
38 | "gitHead": "b893495a21b7d761d895aac9e89666875c315e7b"
39 | }
40 |
--------------------------------------------------------------------------------
/packages/core/src/index.ts:
--------------------------------------------------------------------------------
1 | export {
2 | createStarknetReactRoot,
3 | StarknetReactProvider,
4 | useStarknetReact,
5 | getStarknetReactContext,
6 | PRIMARY_KEY,
7 | } from './provider';
8 | export { UnsupportedChainIdError } from './manager';
9 | export { normalizeAccount, normalizeChainId } from './normalizers';
10 |
--------------------------------------------------------------------------------
/packages/core/src/manager.ts:
--------------------------------------------------------------------------------
1 | import { AbstractConnector } from '@web3-starknet-react/abstract-connector';
2 | import { AccountInterface, Provider } from 'starknet';
3 | import { useReducer, useEffect, useCallback, useRef } from 'react';
4 | import { ConnectorUpdate, ConnectorEvent } from '@web3-starknet-react/types';
5 | import warning from 'tiny-warning';
6 |
7 | import { StarknetReactManagerReturn } from './types';
8 | import { normalizeAccount, normalizeChainId } from './normalizers';
9 |
10 | class StaleConnectorError extends Error {
11 | constructor() {
12 | super();
13 | this.name = this.constructor.name;
14 | }
15 | }
16 |
17 | export class UnsupportedChainIdError extends Error {
18 | public constructor(
19 | unsupportedChainId: number,
20 | supportedChainIds?: readonly number[]
21 | ) {
22 | super();
23 | this.name = this.constructor.name;
24 | this.message = `Unsupported chain id: ${unsupportedChainId}. Supported chain ids are: ${supportedChainIds}.`;
25 | }
26 | }
27 |
28 | interface StarknetReactManagerState {
29 | connector?: AbstractConnector;
30 | provider?: Provider;
31 | chainId?: number;
32 | account?: AccountInterface | null;
33 | connectedAddress?: string | null;
34 |
35 | onError?: (error: Error | unknown) => void;
36 |
37 | error?: Error;
38 | }
39 |
40 | enum ActionType {
41 | ACTIVATE_CONNECTOR,
42 | UPDATE,
43 | UPDATE_FROM_ERROR,
44 | ERROR,
45 | ERROR_FROM_ACTIVATION,
46 | DEACTIVATE_CONNECTOR,
47 | }
48 |
49 | interface Action {
50 | type: ActionType;
51 | payload?: any;
52 | }
53 |
54 | function reducer(
55 | state: StarknetReactManagerState,
56 | { type, payload }: Action
57 | ): StarknetReactManagerState {
58 | switch (type) {
59 | case ActionType.ACTIVATE_CONNECTOR: {
60 | const {
61 | connector,
62 | provider,
63 | chainId,
64 | account,
65 | connectedAddress,
66 | onError,
67 | } = payload;
68 | return {
69 | connector,
70 | provider,
71 | chainId,
72 | account,
73 | connectedAddress,
74 | onError,
75 | };
76 | }
77 | case ActionType.UPDATE: {
78 | const { provider, chainId, account, connectedAddress } = payload;
79 | return {
80 | ...state,
81 | ...(provider === undefined ? {} : { provider }),
82 | ...(chainId === undefined ? {} : { chainId }),
83 | ...(account === undefined ? {} : { account }),
84 | ...(connectedAddress === undefined ? {} : { connectedAddress }),
85 | };
86 | }
87 | case ActionType.UPDATE_FROM_ERROR: {
88 | const { provider, chainId, account, connectedAddress } = payload;
89 | return {
90 | ...state,
91 | ...(provider === undefined ? {} : { provider }),
92 | ...(chainId === undefined ? {} : { chainId }),
93 | ...(account === undefined ? {} : { account }),
94 | ...(connectedAddress === undefined ? {} : { connectedAddress }),
95 | error: undefined,
96 | };
97 | }
98 | case ActionType.ERROR: {
99 | const { error } = payload;
100 | const { connector, onError } = state;
101 | return {
102 | connector,
103 | error,
104 | onError,
105 | };
106 | }
107 | case ActionType.ERROR_FROM_ACTIVATION: {
108 | const { connector, error } = payload;
109 | return {
110 | connector,
111 | error,
112 | };
113 | }
114 | case ActionType.DEACTIVATE_CONNECTOR: {
115 | return {};
116 | }
117 | }
118 | }
119 |
120 | async function augmentConnectorUpdate(
121 | connector: AbstractConnector,
122 | update: ConnectorUpdate
123 | ): Promise> {
124 | const provider =
125 | update.provider === undefined
126 | ? await connector.getProvider()
127 | : update.provider;
128 | const [_chainId, _account, _connectedAddress] = (await Promise.all([
129 | update.chainId === undefined ? connector.getChainId() : update.chainId,
130 | update.account === undefined ? connector.getAccount() : update.account,
131 | update.connectedAddress === undefined
132 | ? connector.getConnectedAddress()
133 | : update.connectedAddress,
134 | ])) as [
135 | Required['chainId'],
136 | Required['account'],
137 | Required['connectedAddress']
138 | ];
139 |
140 | const chainId = normalizeChainId(_chainId);
141 | if (
142 | !!connector.supportedChainIds &&
143 | !connector.supportedChainIds.includes(chainId)
144 | ) {
145 | throw new UnsupportedChainIdError(chainId, connector.supportedChainIds);
146 | }
147 | const connectedAddress =
148 | _connectedAddress === null
149 | ? _connectedAddress
150 | : normalizeAccount(_connectedAddress);
151 |
152 | return {
153 | provider,
154 | chainId,
155 | account: _account,
156 | connectedAddress,
157 | };
158 | }
159 |
160 | export function useStarknetReactManager(): StarknetReactManagerReturn {
161 | const [state, dispatch] = useReducer(reducer, {});
162 | const {
163 | connector,
164 | provider,
165 | chainId,
166 | account,
167 | connectedAddress,
168 | onError,
169 | error,
170 | } = state;
171 |
172 | const updateBusterRef = useRef(-1);
173 | updateBusterRef.current += 1;
174 |
175 | const activate = useCallback(
176 | async (
177 | connector: AbstractConnector,
178 | onError?: (error: Error | unknown) => void,
179 | throwErrors: boolean = false
180 | ): Promise => {
181 | const updateBusterInitial = updateBusterRef.current;
182 |
183 | let activated = false;
184 | try {
185 | const update = await connector.activate().then(
186 | (update): ConnectorUpdate => {
187 | activated = true;
188 | return update;
189 | }
190 | );
191 |
192 | const augmentedUpdate = await augmentConnectorUpdate(connector, update);
193 |
194 | if (updateBusterRef.current > updateBusterInitial) {
195 | throw new StaleConnectorError();
196 | }
197 | dispatch({
198 | type: ActionType.ACTIVATE_CONNECTOR,
199 | payload: { connector, ...augmentedUpdate, onError },
200 | });
201 | } catch (error) {
202 | if (error instanceof StaleConnectorError) {
203 | activated && connector.deactivate();
204 | warning(false, `Suppressed stale connector activation ${connector}`);
205 | } else if (throwErrors) {
206 | activated && connector.deactivate();
207 | throw error;
208 | } else if (onError) {
209 | activated && connector.deactivate();
210 | onError(error);
211 | } else {
212 | // we don't call activated && connector.deactivate() here because it'll be handled in the useEffect
213 | dispatch({
214 | type: ActionType.ERROR_FROM_ACTIVATION,
215 | payload: { connector, error },
216 | });
217 | }
218 | }
219 | },
220 | []
221 | );
222 |
223 | const setError = useCallback((error: Error): void => {
224 | dispatch({ type: ActionType.ERROR, payload: { error } });
225 | }, []);
226 |
227 | const deactivate = useCallback((): void => {
228 | dispatch({ type: ActionType.DEACTIVATE_CONNECTOR });
229 | }, []);
230 |
231 | const handleUpdate = useCallback(
232 | async (update: ConnectorUpdate): Promise => {
233 | if (!connector) {
234 | throw Error(
235 | "This should never happen, it's just so Typescript stops complaining"
236 | );
237 | }
238 |
239 | const updateBusterInitial = updateBusterRef.current;
240 |
241 | // updates are handled differently depending on whether the connector is active vs in an error state
242 | if (!error) {
243 | const chainId =
244 | update.chainId === undefined
245 | ? undefined
246 | : normalizeChainId(update.chainId);
247 | if (
248 | chainId !== undefined &&
249 | !!connector.supportedChainIds &&
250 | !connector.supportedChainIds.includes(chainId)
251 | ) {
252 | const error = new UnsupportedChainIdError(
253 | chainId,
254 | connector.supportedChainIds
255 | );
256 | onError
257 | ? onError(error)
258 | : dispatch({ type: ActionType.ERROR, payload: { error } });
259 | } else {
260 | const connectedAddress =
261 | typeof update.connectedAddress === 'string'
262 | ? normalizeAccount(update.connectedAddress)
263 | : update.connectedAddress;
264 | dispatch({
265 | type: ActionType.UPDATE,
266 | payload: {
267 | provider: update.provider,
268 | account: update.account,
269 | connectedAddress,
270 | chainId,
271 | },
272 | });
273 | }
274 | } else {
275 | try {
276 | const augmentedUpdate = await augmentConnectorUpdate(
277 | connector,
278 | update
279 | );
280 |
281 | if (updateBusterRef.current > updateBusterInitial) {
282 | throw new StaleConnectorError();
283 | }
284 | dispatch({
285 | type: ActionType.UPDATE_FROM_ERROR,
286 | payload: augmentedUpdate,
287 | });
288 | } catch (error) {
289 | if (error instanceof StaleConnectorError) {
290 | warning(
291 | false,
292 | `Suppressed stale connector update from error state ${connector} ${update}`
293 | );
294 | } else {
295 | // though we don't have to, we're re-circulating the new error
296 | onError
297 | ? onError(error)
298 | : dispatch({ type: ActionType.ERROR, payload: { error } });
299 | }
300 | }
301 | }
302 | },
303 | [connector, error, onError]
304 | );
305 | const handleError = useCallback(
306 | (error: Error): void => {
307 | onError
308 | ? onError(error)
309 | : dispatch({ type: ActionType.ERROR, payload: { error } });
310 | },
311 | [onError]
312 | );
313 | const handleDeactivate = useCallback((): void => {
314 | dispatch({ type: ActionType.DEACTIVATE_CONNECTOR });
315 | }, []);
316 |
317 | // ensure that connectors which were set are deactivated
318 | useEffect((): (() => void) => {
319 | return () => {
320 | if (connector) {
321 | connector.deactivate();
322 | }
323 | };
324 | }, [connector]);
325 |
326 | // ensure that events emitted from the set connector are handled appropriately
327 | useEffect((): (() => void) => {
328 | if (connector) {
329 | connector
330 | .on(ConnectorEvent.Update, handleUpdate)
331 | .on(ConnectorEvent.Error, handleError)
332 | .on(ConnectorEvent.Deactivate, handleDeactivate);
333 | }
334 |
335 | return () => {
336 | if (connector) {
337 | connector
338 | .off(ConnectorEvent.Update, handleUpdate)
339 | .off(ConnectorEvent.Error, handleError)
340 | .off(ConnectorEvent.Deactivate, handleDeactivate);
341 | }
342 | };
343 | }, [connector, handleUpdate, handleError, handleDeactivate]);
344 |
345 | return {
346 | connector,
347 | provider,
348 | chainId,
349 | account,
350 | connectedAddress,
351 | activate,
352 | setError,
353 | deactivate,
354 | error,
355 | };
356 | }
357 |
--------------------------------------------------------------------------------
/packages/core/src/normalizers.ts:
--------------------------------------------------------------------------------
1 | import invariant from 'tiny-invariant';
2 | import { validateAndParseAddress } from 'starknet';
3 |
4 | export function normalizeChainId(chainId: string | number): number {
5 | if (typeof chainId === 'string') {
6 | chainId = chainId.replace(/^Ox/, '0x');
7 |
8 | const parsedChainId = Number.parseInt(
9 | chainId,
10 | chainId.trim().substring(0, 2) === '0x' ? 16 : 10
11 | );
12 | invariant(
13 | !Number.isNaN(parsedChainId),
14 | `chainId ${chainId} is not an integer`
15 | );
16 | return parsedChainId;
17 | } else {
18 | invariant(
19 | Number.isInteger(chainId),
20 | `chainId ${chainId} is not an integer`
21 | );
22 | return chainId;
23 | }
24 | }
25 |
26 | export function normalizeAccount(address: string): string {
27 | const starknetAddress = validateAndParseAddress(address);
28 |
29 | return starknetAddress;
30 | }
31 |
--------------------------------------------------------------------------------
/packages/core/src/provider.tsx:
--------------------------------------------------------------------------------
1 | import React, { createContext, useContext, useMemo } from 'react';
2 | import invariant from 'tiny-invariant';
3 |
4 | import { StarknetReactContextInterface } from './types';
5 | import { useStarknetReactManager } from './manager';
6 | import { Provider } from 'starknet';
7 |
8 | export const PRIMARY_KEY = 'primary';
9 |
10 | const CONTEXTS: {
11 | [key: string]: React.Context;
12 | } = {};
13 |
14 | interface StarknetReactProviderArguments {
15 | getLibrary: (
16 | provider?: Provider,
17 | connector?: Required['connector']
18 | ) => Provider;
19 | children: any;
20 | }
21 |
22 | export function createStarknetReactRoot(
23 | key: string
24 | ): (args: StarknetReactProviderArguments) => JSX.Element {
25 | invariant(!CONTEXTS[key], `A root already exists for provided key ${key}`);
26 |
27 | CONTEXTS[key] = createContext({
28 | activate: async () => {
29 | invariant(false, 'No found');
30 | },
31 | setError: () => {
32 | invariant(false, 'No found.');
33 | },
34 | deactivate: () => {
35 | invariant(false, 'No found.');
36 | },
37 | active: false,
38 | });
39 |
40 | CONTEXTS[key].displayName = `StarknetReactContext - ${key}`;
41 |
42 | const Provider = CONTEXTS[key].Provider;
43 |
44 | return function StarknetReactProvider({
45 | getLibrary,
46 | children,
47 | }: StarknetReactProviderArguments): JSX.Element {
48 | const {
49 | connector,
50 | provider,
51 | chainId,
52 | account,
53 | connectedAddress,
54 | activate,
55 | setError,
56 | deactivate,
57 | error,
58 | } = useStarknetReactManager();
59 |
60 | const active =
61 | connector !== undefined &&
62 | chainId !== undefined &&
63 | account !== undefined &&
64 | !!!error;
65 |
66 | const library = useMemo(
67 | () =>
68 | active &&
69 | chainId !== undefined &&
70 | Number.isInteger(chainId) &&
71 | !!connector
72 | ? getLibrary(provider, connector)
73 | : undefined,
74 | [active, getLibrary, provider, connector, chainId]
75 | );
76 |
77 | const starknetReactContext: StarknetReactContextInterface = {
78 | connector,
79 | library,
80 | activate,
81 | active,
82 | deactivate,
83 | setError,
84 | account,
85 | connectedAddress,
86 | chainId,
87 | error,
88 | };
89 |
90 | return {children};
91 | };
92 | }
93 |
94 | export const StarknetReactProvider = createStarknetReactRoot(PRIMARY_KEY);
95 |
96 | export function getStarknetReactContext(
97 | key: string = PRIMARY_KEY
98 | ): React.Context> {
99 | invariant(Object.keys(CONTEXTS).includes(key), `Invalid key ${key}`);
100 | return CONTEXTS[key];
101 | }
102 |
103 | export function useStarknetReact(
104 | key?: string
105 | ): StarknetReactContextInterface {
106 | return useContext(getStarknetReactContext(key));
107 | }
108 |
--------------------------------------------------------------------------------
/packages/core/src/types.ts:
--------------------------------------------------------------------------------
1 | import { AbstractConnector } from '@web3-starknet-react/abstract-connector';
2 | import { Provider, AccountInterface } from 'starknet';
3 |
4 | export interface StarknetReactManagerFunction {
5 | activate: (
6 | connector: AbstractConnector,
7 | onError?: (error: Error | unknown) => void,
8 | throwsError?: boolean
9 | ) => Promise;
10 | setError: (error: Error) => void;
11 | deactivate: () => void;
12 | }
13 |
14 | export interface StarknetReactManagerReturn
15 | extends StarknetReactManagerFunction {
16 | connector?: AbstractConnector;
17 | provider?: Provider;
18 | chainId?: number;
19 | account?: AccountInterface | null;
20 | connectedAddress?: string | null;
21 |
22 | error?: Error;
23 | }
24 |
25 | export interface StarknetReactContextInterface
26 | extends StarknetReactManagerFunction {
27 | connector?: AbstractConnector;
28 | library?: T;
29 | chainId?: number;
30 | account?: AccountInterface | null;
31 | connectedAddress?: string | null;
32 |
33 | active: boolean;
34 | error?: Error;
35 | }
36 |
--------------------------------------------------------------------------------
/packages/core/test/normalizers.test.ts:
--------------------------------------------------------------------------------
1 | import { normalizeAccount } from '../src/normalizers';
2 |
3 | describe('normalizers test', () => {
4 | it('checks if normalizeAcccount works with 64bit address string', () => {
5 | let account =
6 | '0x6eff1d71068df8e6677f59a556151c56ed13e14ad431a9bef6fcb3fc5e6fa7';
7 |
8 | const normalizedAccount = normalizeAccount(account);
9 |
10 | expect(normalizedAccount).toBe(
11 | '0x006eff1d71068df8e6677f59a556151c56ed13e14ad431a9bef6fcb3fc5e6fa7'
12 | );
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/packages/core/test/provider.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import * as ReactDOM from 'react-dom';
3 |
4 | import { StarknetReactProvider } from '../src/provider';
5 | import { Provider } from 'starknet';
6 |
7 | function getLibrary(provider?: Provider) {
8 | const library = new Provider(provider);
9 |
10 | return library;
11 | }
12 |
13 | function App() {
14 | return (
15 |
16 | test!
17 |
18 | );
19 | }
20 |
21 | describe('it', () => {
22 | it('renders without crashing', () => {
23 | const div = document.createElement('div');
24 | ReactDOM.render(, div);
25 | ReactDOM.unmountComponentAtNode(div);
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/packages/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": ["src", "test"],
4 | "compilerOptions": {
5 | "rootDir": "./",
6 | "baseUrl": "./",
7 | "paths": {
8 | "*": ["src/*", "node_modules/*"]
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/types/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | .DS_Store
3 | node_modules
4 | .cache
5 | .rts2_cache_cjs
6 | .rts2_cache_esm
7 | .rts2_cache_umd
8 | .rts2_cache_system
9 | dist
10 |
--------------------------------------------------------------------------------
/packages/types/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 dhruvkelawala
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.
22 |
--------------------------------------------------------------------------------
/packages/types/README.md:
--------------------------------------------------------------------------------
1 | # @web3-starknet-react/types
2 |
3 | Please visit the [parent `web3-starknet-react` repository](https://github.com/dhruvkelawala/web3-starknet-react/) for documentation and details on this package.
4 |
--------------------------------------------------------------------------------
/packages/types/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@web3-starknet-react/types",
3 | "publishConfig": {
4 | "access": "public"
5 | },
6 | "version": "2.1.0",
7 | "author": "Dhruv Kelawala ",
8 | "main": "dist/index.js",
9 | "module": "dist/types.esm.js",
10 | "typings": "dist/index.d.ts",
11 | "files": [
12 | "dist"
13 | ],
14 | "scripts": {
15 | "start": "tsdx watch",
16 | "build": "tsdx build",
17 | "test": "tsdx test --env=jsdom",
18 | "lint": "tsdx lint"
19 | },
20 | "peerDependencies": {
21 | "react": ">=16"
22 | },
23 | "repository": {
24 | "type": "git",
25 | "url": "git://github.com/dhruvkelawala/web3-starknet-react.git"
26 | },
27 | "gitHead": "b893495a21b7d761d895aac9e89666875c315e7b"
28 | }
29 |
--------------------------------------------------------------------------------
/packages/types/src/index.ts:
--------------------------------------------------------------------------------
1 | import { AccountInterface, Provider } from 'starknet'
2 |
3 | export interface AbstractConnectorArguments {
4 | supportedChainIds?: number[]
5 | }
6 |
7 | export interface ConnectorUpdate {
8 | provider?: Provider
9 | chainId?: T
10 | account?: AccountInterface | null
11 | connectedAddress?: string | null
12 | }
13 |
14 | export enum ConnectorEvent {
15 | Update = 'StarknetReactUpdate',
16 | Error = 'StarknetReactError',
17 | Deactivate = 'StarknetReactDeactivate'
18 | }
19 |
--------------------------------------------------------------------------------
/packages/types/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["src", "types", "test"],
3 | "compilerOptions": {
4 | "target": "es5",
5 | "module": "esnext",
6 | "lib": ["dom", "esnext"],
7 | "importHelpers": true,
8 | "declaration": true,
9 | "sourceMap": true,
10 | "rootDir": "./",
11 | "strict": true,
12 | "noImplicitAny": true,
13 | "strictNullChecks": true,
14 | "strictFunctionTypes": true,
15 | "strictPropertyInitialization": true,
16 | "noImplicitThis": true,
17 | "alwaysStrict": true,
18 | "noUnusedLocals": true,
19 | "noUnusedParameters": true,
20 | "noImplicitReturns": true,
21 | "noFallthroughCasesInSwitch": true,
22 | "moduleResolution": "node",
23 | "baseUrl": "./",
24 | "paths": {
25 | "*": ["src/*", "node_modules/*"]
26 | },
27 | "jsx": "react",
28 | "esModuleInterop": true
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "esnext",
5 | "lib": ["dom", "esnext"],
6 | "importHelpers": true,
7 | "declaration": true,
8 | "sourceMap": true,
9 | "strict": true,
10 | "noImplicitAny": true,
11 | "noImplicitReturns": true,
12 | "strictNullChecks": true,
13 | "strictFunctionTypes": true,
14 | "strictPropertyInitialization": true,
15 | "noImplicitThis": true,
16 | "alwaysStrict": true,
17 | "noUnusedLocals": true,
18 | "noUnusedParameters": true,
19 | "noFallthroughCasesInSwitch": true,
20 | "moduleResolution": "node",
21 | "jsx": "react",
22 | "esModuleInterop": true
23 | }
24 | }
25 |
--------------------------------------------------------------------------------