├── .env.sample ├── .eslintrc.json ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .nvmrc ├── LICENSE ├── README.md ├── SECURITY.md ├── app ├── (pages) │ ├── (authorized) │ │ └── wallets │ │ │ ├── [id] │ │ │ ├── activity │ │ │ │ └── [transactionId] │ │ │ │ │ └── page.tsx │ │ │ ├── deposit │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── page.tsx │ │ │ └── send │ │ │ │ ├── [tokenName] │ │ │ │ └── page.tsx │ │ │ │ └── page.tsx │ │ │ ├── create │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ ├── layout.tsx │ ├── page.tsx │ ├── signin │ │ └── page.tsx │ └── signup │ │ └── page.tsx ├── api │ ├── auth │ │ └── [...nextauth] │ │ │ └── route.ts │ └── ping │ │ └── route.ts ├── axios │ ├── axios.ts │ ├── faucet.ts │ ├── index.ts │ ├── tokens.ts │ ├── transactions.ts │ ├── users.ts │ └── wallets.ts ├── components │ ├── Authentication │ │ └── AuthenticationForm.tsx │ ├── Buttons │ │ ├── BackButton.tsx │ │ ├── CopyButton.tsx │ │ └── index.ts │ ├── Cards │ │ └── TokenCard.tsx │ ├── Content.tsx │ ├── Footer.tsx │ ├── Loading │ │ └── LoadingWrapper.tsx │ ├── Providers │ │ ├── ClientProviders.tsx │ │ ├── SendTokenProvider.tsx │ │ └── W3sProvider.tsx │ ├── TextField.tsx │ └── index.ts ├── containers │ ├── Deposit.tsx │ ├── WalletActivity.tsx │ ├── WalletActivityDetails.tsx │ ├── WalletDetails.tsx │ └── Wallets │ │ └── Send │ │ ├── SelectToken.tsx │ │ ├── SendToken.tsx │ │ ├── SendTokenConfirm.tsx │ │ ├── SendTokenForm.tsx │ │ ├── SendTokenSummary.tsx │ │ └── index.ts ├── globals.css ├── icon.ico ├── shared │ ├── types.ts │ └── utils.ts └── types │ └── next-auth.d.ts ├── next.config.js ├── package.json ├── postcss.config.js ├── public ├── Avax.svg ├── CircleLogo.svg ├── CircleLogoWithName.svg ├── ErrorIcon.svg ├── Eth.svg ├── Faucet.svg ├── Github.svg ├── Matic.svg ├── NoActivity.svg ├── NoTokens.svg ├── Solana.svg ├── Success.gif └── USDC.svg ├── tailwind.config.ts ├── tsconfig.json └── yarn.lock /.env.sample: -------------------------------------------------------------------------------- 1 | # Located here https://console.circle.com/wallets/user/configurator 2 | NEXT_PUBLIC_APP_ID="[APP_ID goes here]" 3 | # hosted backend url 4 | NEXT_PUBLIC_BASE_URL="http://localhost:8080" 5 | 6 | # canonical hosted url 7 | NEXTAUTH_URL="http://localhost:3000" 8 | 9 | NODE_ENV=development 10 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "prettier"] 3 | } 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | on: 3 | pull_request: 4 | push: 5 | branches: [master] 6 | jobs: 7 | scan: 8 | if: github.event_name == 'pull_request' 9 | uses: circlefin/circle-public-github-workflows/.github/workflows/pr-scan.yaml@v1 10 | 11 | release-sbom: 12 | if: github.event_name == 'push' 13 | uses: circlefin/circle-public-github-workflows/.github/workflows/attach-release-assets.yaml@v1 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | .env 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | 39 | # terraform 40 | .terraform/ 41 | .terraform.lock.hcl 42 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v20.18.2 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | © Circle Internet Financial, LTD 2024. All rights reserved. 179 | 180 | SPDX-License-Identifier: Apache-2.0 181 | 182 | Licensed under the Apache License, Version 2.0 (the "License"); 183 | you may not use this file except in compliance with the License. 184 | You may obtain a copy of the License at 185 | 186 | http://www.apache.org/licenses/LICENSE-2.0 187 | 188 | Unless required by applicable law or agreed to in writing, software 189 | distributed under the License is distributed on an "AS IS" BASIS, 190 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 191 | See the License for the specific language governing permissions and 192 | limitations under the License. 193 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Circle User-Controlled Wallets Sample App - Frontend UI 2 | 3 | Check out the [live demo](https://user-controlled-wallets-sample-app.circle.com/) first to see what to expect! 4 | 5 | ## Overview 6 | 7 | User-Controlled Wallets Sample App showcases the integration of Circle's Web3 Services products (Web SDK, [Smart Contract Accounts (SCA)](https://developers.circle.com/w3s/docs/programmable-wallets-account-types) user-controlled wallets, gasless transactions). You can download and easily run and configure for your own projects. The use case it will be supporting is integrating user-controlled wallets into an existing web application, so that you can provide wallets to your end users. 8 | 9 | This is a sample frontend UI that plays a part in the larger Sample App project. We use [Circle Web3 Services Web SDK](https://developers.circle.com/w3s/docs/web) to protect users' sensitive data like PINs or security answers, which will be used to interact with Circle APIs for [User-Controlled Wallets](https://developers.circle.com/w3s/reference/createuser). 10 | 11 | ## Prerequisites 12 | 13 | 1. Sign up for [Circle's Dev Console](https://developers.circle.com/w3s/docs/circle-developer-account) to obtain an [App ID](https://console.circle.com/wallets/user/configurator). Side Bar Navigation: Programmable Wallets / User Controlled / Configurator 14 | 15 | 2. Install [nvm](https://github.com/nvm-sh/nvm), [openssl](https://formulae.brew.sh/formula/openssl@3), and [yarn](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable), these are required development tools. 16 | 17 | 3. **_Important:_** Set up the [Sample Server](https://github.com/circlefin/w3s-sample-user-controlled-server-node) as well to get the end-to-end experience. Please be aware that the [SDK user token](https://developers.circle.com/w3s/reference/getusertoken) will expire after 60 minutes. 18 | 19 | ## Configure the Sample App 20 | 21 | 1. Run `yarn env:config`, and you will see a `.env` file generated in the root directory 22 | 2. Paste your [App ID](https://console.circle.com/wallets/user/configurator) with `[APP_ID goes here]` in the `.env` file. 23 | 24 | ## Get Started 25 | 26 | Run the following commands to start the UI at `localhost:3000`: 27 | 28 | ``` bash 29 | nvm use 30 | yarn install 31 | yarn dev 32 | ``` 33 | 34 | 1. `nvm use`: set node version. 35 | 2. `yarn install`: install dependencies. 36 | 3. `yarn dev`: run the server, hot reload is supported. 37 | 38 | ## Architecture 39 | 40 | The frontend UI will play the role as `Your Application`, see [details](). 41 | ![image](https://files.readme.io/a2a1678-SDK_UserC_Wallets_Sequence__Detailed2x.png) 42 | 43 | ## Code Structure 44 | 45 | We use [Next.js](https://nextjs.org/) as [React](https://react.dev/) framework and [Joy UI](https://mui.com/joy-ui/getting-started/) as React component library. 46 | 47 | - The main logic to interact with the Circle Web3 Services Web SDK is going to be in our client side component in `app/components`: 48 | - `providers/W3sProvider.tsx`: holds the value to setup and instantiate a SDK instance. Part of the setup is authorizing with the App ID, 49 | 50 | ```javascript 51 | webClient?.setAppSettings({ 52 | appId, 53 | }); 54 | ``` 55 | 56 | setting up the forgot pin callback, 57 | 58 | ```javascript 59 | webClient?.setOnForgotPin(async () => { 60 | const response = await axios.post<{ challengeId: string }>( 61 | "/users/pin/restore", 62 | ); 63 | if (response.data) { 64 | webClient.execute(response.data.challengeId); 65 | } 66 | }); 67 | ``` 68 | 69 | and authenticating with the user token + encryption key. 70 | 71 | ```javascript 72 | client.setAuthentication({ 73 | userToken: currUser.userToken, 74 | encryptionKey: currUser.encryptionKey, 75 | }); 76 | ``` 77 | 78 | - `Authentication/AuthenticationForm.tsx` has an example of executing a challenge ID and cutomizing behavior based off a successful execution. 79 | 80 | ```javascript 81 | client.execute(session.user.challengeId, (error, result) => { 82 | if (error) { 83 | setFormMessage("An error occurred on PIN Setup. Please try again."); 84 | } else if (result) { 85 | router.push("/wallets"); 86 | } 87 | }); 88 | ``` 89 | 90 | - `app/(pages)` contains all the server side pages of this Next.js application. Any directory wrapped in `()` is a [route grouping](https://nextjs.org/docs/app/building-your-application/routing/route-groups). 91 | - `(authorized)/`: all server side pages that can only be viewed if the user has a valid session. Check out `(authorized)/layout.ts` to see session validation. 92 | - The above are the most important files to get an understanding of this application. All other files are specific to this application and not crucial to using Circle Web3 Services Web SDK. 93 | 94 | **Happy Coding!** 95 | 96 | ## Additional Resources 97 | 98 | - [Circle Web3 Services Web SDK](https://developers.circle.com/w3s/docs/web-sdk-ui-customizations) supports UI customization, check [more examples](https://github.com/circlefin/w3s-pw-web-sdk). 99 | - Need help: 100 | - Join our Discord community: 101 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | Please do not file public issues on Github for security vulnerabilities. All security vulnerabilities should be reported to Circle privately, through Circle's [Vulnerability Disclosure Program](https://hackerone.com/circle). Please read through the program policy before submitting a report. 6 | -------------------------------------------------------------------------------- /app/(pages)/(authorized)/wallets/[id]/activity/[transactionId]/page.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import { WalletActivityDetails } from "@/app/containers/WalletActivityDetails"; 18 | 19 | type WalletActivityDetailsParams = { 20 | params: { 21 | /* 22 | * Wallet id. 23 | */ 24 | id: string; 25 | /* 26 | * Transaction id. 27 | */ 28 | transactionId: string; 29 | }; 30 | }; 31 | 32 | export default function WalletActivityDetailsPage({ 33 | params, 34 | }: WalletActivityDetailsParams) { 35 | return ( 36 | 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /app/(pages)/(authorized)/wallets/[id]/deposit/page.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import { Deposit } from "@/app/containers/Deposit"; 18 | 19 | type DepositParams = { 20 | params: { 21 | /* 22 | * Wallet id. 23 | */ 24 | id: string; 25 | }; 26 | }; 27 | 28 | export default function DepositPage({ params }: DepositParams) { 29 | return ; 30 | } 31 | -------------------------------------------------------------------------------- /app/(pages)/(authorized)/wallets/[id]/layout.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | "use client"; 18 | import { CopyButton, useW3sContext } from "@/app/components"; 19 | import { blockchainMeta, getAddressAbbreviation } from "@/app/shared/utils"; 20 | import { 21 | CircularProgress, 22 | Dropdown, 23 | IconButton, 24 | Menu, 25 | MenuButton, 26 | MenuItem, 27 | Tooltip, 28 | } from "@mui/joy"; 29 | import { signOut } from "next-auth/react"; 30 | import { useRestorePinMutation, useWallet, useWallets } from "@/app/axios"; 31 | import Image from "next/image"; 32 | import { 33 | ArrowRightStartOnRectangleIcon, 34 | Cog6ToothIcon, 35 | EllipsisVerticalIcon, 36 | PlusIcon, 37 | QuestionMarkCircleIcon, 38 | } from "@heroicons/react/16/solid"; 39 | import { useRouter } from "next/navigation"; 40 | import { blockchainNames } from "@/app/shared/types"; 41 | 42 | type WalletLayoutParams = { 43 | /* 44 | * Wallet id. 45 | */ 46 | id: string; 47 | }; 48 | 49 | export default function WalletLayout({ 50 | children, 51 | params, 52 | }: { 53 | children: React.ReactNode; 54 | params: WalletLayoutParams; 55 | }) { 56 | const router = useRouter(); 57 | const { client } = useW3sContext(); 58 | const { data: wallet } = useWallet(params.id); 59 | const { data: wallets } = useWallets(); 60 | const restorePin = useRestorePinMutation(); 61 | 62 | const blockchainInfo = blockchainMeta(wallet?.data.wallet.blockchain); 63 | const walletAddress = wallet?.data.wallet.address ?? ""; 64 | 65 | const handleChangePin = async () => { 66 | const challengeId = await restorePin.mutateAsync(); 67 | 68 | client?.execute(challengeId, (error) => { 69 | if (!error) { 70 | // handle successful changing of pin. 71 | alert("Your pin has successfully been reset"); 72 | } 73 | 74 | // handle change pin error (e.g. user closed out, bad answers, etc). 75 | }); 76 | }; 77 | 78 | const handleSignOut = () => 79 | signOut({ 80 | redirect: true, 81 | callbackUrl: process.env.NEXTAUTH_URL, 82 | }); 83 | 84 | return ( 85 | <> 86 | {/* Wallet Address */} 87 |
88 | 89 | 90 | {blockchainInfo.svg ? ( 91 | blockchain 97 | ) : ( 98 | 99 | )} 100 | 101 | 102 | 103 | 107 | 108 | 109 | 114 | {restorePin.isLoading ? ( 115 | 116 | ) : ( 117 | 118 | )} 119 | 120 | 121 | {wallets?.data.wallets.map((wallet) => { 122 | // hide currently selected wallet 123 | if (wallet.id === params.id) return null; 124 | return ( 125 | { 128 | router.push(`/wallets/${wallet.id}`); 129 | }} 130 | > 131 | {`${wallet.blockchain}-icon`}{" "} 137 | {blockchainNames[wallet.blockchain]} 138 | 139 | ); 140 | })} 141 | { 143 | router.push("/wallets/create"); 144 | }} 145 | > 146 | Create new wallet 147 | 148 | 149 | Change Pin 150 | 151 | 152 | 153 | Sign out 154 | 155 | 156 | 157 |
158 | {children} 159 | 160 | ); 161 | } 162 | -------------------------------------------------------------------------------- /app/(pages)/(authorized)/wallets/[id]/page.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import { WalletDetails } from "@/app/containers/WalletDetails"; 18 | 19 | type WalletDetailsParams = { 20 | params: { 21 | /* 22 | * Wallet id. 23 | */ 24 | id: string; 25 | }; 26 | }; 27 | 28 | export default function WalletDetailsPage({ params }: WalletDetailsParams) { 29 | return ; 30 | } 31 | -------------------------------------------------------------------------------- /app/(pages)/(authorized)/wallets/[id]/send/[tokenName]/page.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import { SendToken } from "@/app/containers/Wallets/Send"; 18 | 19 | interface WalletSendTokenProps { 20 | params: { 21 | /* 22 | * Wallet id. 23 | */ 24 | id: string; 25 | /* 26 | * Token name. 27 | */ 28 | tokenName: string; 29 | }; 30 | } 31 | 32 | export default function WalletSendTokenPage({ params }: WalletSendTokenProps) { 33 | return ( 34 | 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /app/(pages)/(authorized)/wallets/[id]/send/page.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import { SelectToken } from "@/app/containers/Wallets/Send"; 18 | 19 | interface WalletSendProps { 20 | params: { 21 | /* 22 | * Wallet id. 23 | */ 24 | id: string; 25 | }; 26 | } 27 | 28 | export default function WalletSendPage({ params }: WalletSendProps) { 29 | return ; 30 | } 31 | -------------------------------------------------------------------------------- /app/(pages)/(authorized)/wallets/create/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useCreateWallet, useWallets } from "@/app/axios"; 4 | import { BackButton, Content, useW3sContext } from "@/app/components"; 5 | import { BlockchainEnum, blockchainNames } from "@/app/shared/types"; 6 | import { blockchainMeta } from "@/app/shared/utils"; 7 | import { CheckIcon } from "@heroicons/react/16/solid"; 8 | import { Button, Radio, RadioGroup, Sheet, Typography } from "@mui/joy"; 9 | import Image from "next/image"; 10 | import { useRouter } from "next/navigation"; 11 | import { useEffect, useRef, useState } from "react"; 12 | 13 | export default function CreateWalletPage() { 14 | const createWalletMutation = useCreateWallet(); 15 | const router = useRouter(); 16 | const { client } = useW3sContext(); 17 | 18 | const previousWalletsCount = useRef(0); // Ref to hold the previous value 19 | 20 | const [selected, setSelected] = useState(); 21 | const [loading, setLoading] = useState(false); 22 | 23 | const walletsQuery = useWallets( 24 | undefined, 25 | createWalletMutation.status === "success" ? 1000 : undefined, 26 | ); 27 | 28 | useEffect(() => { 29 | // make sure first wallet is created. 30 | if ( 31 | previousWalletsCount?.current > 0 && 32 | previousWalletsCount.current !== walletsQuery.data?.data.wallets.length 33 | ) { 34 | router.push("/wallets"); 35 | } 36 | 37 | previousWalletsCount.current = walletsQuery.data?.data.wallets.length ?? 0; 38 | }, [router, walletsQuery.data?.data.wallets.length]); 39 | 40 | const createLoading = createWalletMutation.isLoading || loading; 41 | 42 | return ( 43 | 44 | 49 | Select a chain to deploy your wallet 50 | setSelected(e.currentTarget.value as BlockchainEnum)} 54 | > 55 | {[ 56 | BlockchainEnum.MATIC_AMOY, 57 | BlockchainEnum.ETH_SEPOLIA, 58 | BlockchainEnum.AVAX_FUJI, 59 | BlockchainEnum.SOL_DEVNET, 60 | ].map((blockchain) => ( 61 | 68 | wallet.blockchain === blockchain, 73 | ) 74 | } 75 | label={ 76 |
77 | 78 | {`${blockchain}-icon`} 84 | {blockchainNames[blockchain]} 85 | 86 | {selected === blockchain ? : null} 87 |
88 | } 89 | value={blockchain} 90 | disableIcon 91 | className={`w-full p-2`} 92 | slotProps={{ 93 | action({ checked }) { 94 | return { 95 | sx: { 96 | border: "none", 97 | borderRadius: "sm", 98 | }, 99 | className: checked ? `bg-blue-100` : undefined, 100 | }; 101 | }, 102 | }} 103 | /> 104 |
105 | ))} 106 |
107 |
108 | {createLoading && ( 109 | 110 | Please wait while we create your brand new wallet. 111 | 112 | )} 113 | 135 | 136 | ); 137 | } 138 | -------------------------------------------------------------------------------- /app/(pages)/(authorized)/wallets/layout.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import { authOptions, validOnboardStatus } from "@/app/shared/utils"; 18 | import { getServerSession } from "next-auth"; 19 | import { redirect } from "next/navigation"; 20 | 21 | export default async function RootLayout({ 22 | children, 23 | }: Readonly<{ 24 | children: React.ReactNode; 25 | }>) { 26 | const session = await getServerSession(authOptions); 27 | const isValidOnboardStatus = session 28 | ? await validOnboardStatus(session) 29 | : false; 30 | 31 | if (!session || !isValidOnboardStatus) { 32 | redirect("/signin"); 33 | } 34 | 35 | return <>{children}; 36 | } 37 | -------------------------------------------------------------------------------- /app/(pages)/(authorized)/wallets/page.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | "use client"; 18 | import { useRouter } from "next/navigation"; 19 | import { useWallets } from "@/app/axios"; 20 | import { useEffect, useState } from "react"; 21 | import { LoadingWrapper } from "@/app/components"; 22 | import { UseQueryOptions } from "react-query"; 23 | import { Button, Typography } from "@mui/joy"; 24 | import { signOut } from "next-auth/react"; 25 | import Image from "next/image"; 26 | 27 | export default function WalletPage() { 28 | const router = useRouter(); 29 | const [loading, setLoading] = useState(true); 30 | 31 | const refetchIntervalFn: UseQueryOptions["refetchInterval"] = ( 32 | _data, 33 | query, 34 | ) => { 35 | if (query.state.dataUpdateCount < 3) { 36 | return 2000; 37 | } else { 38 | setLoading(false); 39 | return false; 40 | } 41 | }; 42 | const { data: wallets } = useWallets(undefined, refetchIntervalFn); 43 | 44 | useEffect(() => { 45 | if (wallets && wallets.data?.wallets?.length > 0) { 46 | // redirect to the first wallet 47 | const firstWallet = wallets.data.wallets[0]; 48 | const walletId = firstWallet.id; 49 | router.push(`/wallets/${walletId}`); 50 | } 51 | }, [router, wallets]); 52 | 53 | return ( 54 |
55 | 56 | No wallets 63 | 64 | 65 | Something went wrong 66 | 67 | 72 | Sign out and restart the app to try again 73 | 74 | 75 | 78 | 79 |
80 | ); 81 | } 82 | -------------------------------------------------------------------------------- /app/(pages)/layout.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import "../globals.css"; 18 | 19 | import Image from "next/image"; 20 | import React from "react"; 21 | import { Inter } from "next/font/google"; 22 | import { Metadata } from "next"; 23 | 24 | import { ClientProviders, Footer } from "@/app/components"; 25 | import { Button, Typography } from "@mui/joy"; 26 | import { BookOpenIcon } from "@heroicons/react/16/solid"; 27 | 28 | const inter = Inter({ 29 | subsets: ["cyrillic"], 30 | }); 31 | 32 | export default function RootLayout({ 33 | children, 34 | }: Readonly<{ 35 | children: React.ReactNode; 36 | }>) { 37 | return ( 38 | 39 | 40 |
41 | 42 |
43 |
44 | {/* Larger Screen Logo */} 45 |
46 | Circle Logo 53 | 54 | User-Controlled Wallets 55 | 56 |
57 | {children} 58 | {/* Larger Screen Source Code/Docs */} 59 | 90 |
91 |
92 |
93 |
94 |
95 | 96 | 97 | ); 98 | } 99 | 100 | // Adds top banner/borders for 101 | const AppContainer = ({ children }: { children: React.ReactNode }) => ( 102 |
103 | {/* banner for larger screens */} 104 | 105 | Circle Logo{" "} 106 | Your app here 107 | 108 | {/* banner for smaller screens */} 109 |
110 | 111 | Circle Logo 118 | 119 | User-Controlled Wallets 120 | 121 | 122 | 123 | 124 | 128 | 129 | 130 | 134 | 135 | 136 | 137 |
138 | {children} 139 |
140 | ); 141 | 142 | export const metadata: Metadata = { 143 | title: "Programmable Wallet SDK Web Sample App", 144 | description: "An example of how to use Circle's Programmable Wallet SDK", 145 | }; 146 | -------------------------------------------------------------------------------- /app/(pages)/page.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | export default function Home() { 18 | return <>; 19 | } 20 | -------------------------------------------------------------------------------- /app/(pages)/signin/page.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import { authOptions, validOnboardStatus } from "@/app/shared/utils"; 18 | import { AuthenticationForm } from "@/app/components"; 19 | import { getServerSession } from "next-auth"; 20 | import { redirect } from "next/navigation"; 21 | 22 | export default async function SigninPage() { 23 | const session = await getServerSession(authOptions); 24 | const isValidOnboardStatus = session 25 | ? await validOnboardStatus(session) 26 | : false; 27 | 28 | if (session && isValidOnboardStatus) { 29 | redirect("/wallets"); 30 | } 31 | 32 | return ; 33 | } 34 | -------------------------------------------------------------------------------- /app/(pages)/signup/page.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import { getServerSession } from "next-auth"; 18 | 19 | import { redirect } from "next/navigation"; 20 | import { authOptions, validOnboardStatus } from "@/app/shared/utils"; 21 | import { AuthenticationForm } from "@/app/components"; 22 | 23 | export default async function SignupPage() { 24 | const session = await getServerSession(authOptions); 25 | const isValidOnboardStatus = session 26 | ? await validOnboardStatus(session) 27 | : false; 28 | 29 | if (session && isValidOnboardStatus) { 30 | redirect("/wallets"); 31 | } 32 | 33 | return ; 34 | } 35 | -------------------------------------------------------------------------------- /app/api/auth/[...nextauth]/route.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import { authOptions } from "@/app/shared/utils"; 18 | import NextAuth from "next-auth"; 19 | 20 | const handler = NextAuth(authOptions); 21 | 22 | export { handler as GET, handler as POST }; 23 | -------------------------------------------------------------------------------- /app/api/ping/route.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import { NextResponse } from "next/server"; 18 | 19 | /** 20 | * Health check endpoint. 21 | */ 22 | export async function GET() { 23 | return NextResponse.json("pong", { status: 200 }); 24 | } 25 | -------------------------------------------------------------------------------- /app/axios/axios.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import ax, { AxiosError } from "axios"; 18 | import { getSession, signOut } from "next-auth/react"; 19 | 20 | const axios = ax.create({ 21 | baseURL: process.env.NEXT_PUBLIC_BASE_URL, 22 | headers: { 23 | post: { 24 | "Content-Type": "application/json", 25 | }, 26 | }, 27 | }); 28 | 29 | axios.interceptors.request.use(async (request) => { 30 | const tokenDefault = axios.defaults.headers.token; 31 | 32 | // if token header not set 33 | if (!Boolean(tokenDefault)) { 34 | const session = await getSession({ 35 | req: request, 36 | }); 37 | 38 | if (session) { 39 | const bearerToken = `Bearer ${session.user.userToken}`; 40 | request.headers.Authorization = bearerToken; 41 | axios.defaults.headers.Authorization = bearerToken; 42 | } 43 | } 44 | 45 | return request; 46 | }); 47 | 48 | axios.interceptors.response.use(undefined, async (error: unknown) => { 49 | if (error instanceof AxiosError && error.response?.status === 403) { 50 | await signOut({ 51 | callbackUrl: "/signin", 52 | redirect: true, 53 | }); 54 | } 55 | 56 | throw error; 57 | }); 58 | 59 | export { axios }; 60 | -------------------------------------------------------------------------------- /app/axios/faucet.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | "use client"; 18 | import { axios } from "@/app/axios"; 19 | import { useMutation } from "react-query"; 20 | import { FaucetDripInput } from "../shared/types"; 21 | 22 | const faucetDripHelper = (input: FaucetDripInput) => { 23 | return axios.post(`/faucet/drips`, input); 24 | }; 25 | 26 | export const useFaucetDripMutation = () => { 27 | return useMutation(faucetDripHelper); 28 | }; 29 | -------------------------------------------------------------------------------- /app/axios/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | export * from "./axios"; 18 | export * from "./wallets"; 19 | export * from "./transactions"; 20 | export * from "./tokens"; 21 | export * from "./users"; 22 | export * from "./faucet"; 23 | -------------------------------------------------------------------------------- /app/axios/tokens.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | "use client"; 18 | import { axios } from "@/app/axios"; 19 | import { useQuery } from "react-query"; 20 | import { Token } from "../shared/types"; 21 | 22 | const tokenDetailsHelper = async (tokenId: string) => { 23 | const response = await axios.get<{ 24 | token: Token; 25 | }>(`/tokens/${tokenId}`); 26 | 27 | return response.data.token; 28 | }; 29 | 30 | export const useTokenDetailsQuery = (tokenId: string, enabled?: boolean) => { 31 | return useQuery({ 32 | queryKey: ["getTokenDetails", tokenId], 33 | queryFn: () => tokenDetailsHelper(tokenId), 34 | enabled: enabled ?? true 35 | }); 36 | }; 37 | -------------------------------------------------------------------------------- /app/axios/transactions.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | "use client"; 18 | import { axios } from "@/app/axios"; 19 | import { useQuery } from "react-query"; 20 | import { useMutation } from "react-query"; 21 | import { 22 | EstimateFeeInput, 23 | EstimateFeeResponse, 24 | Transaction, 25 | } from "../shared/types"; 26 | 27 | // Get transactions query 28 | const transactionsHelper = async (walletId: string) => { 29 | const response = await axios.get<{ 30 | transactions: Transaction[]; 31 | }>(`/transactions`, { params: { walletIds: [walletId] } }); 32 | 33 | return response.data.transactions; 34 | }; 35 | 36 | export const useTransactionsQuery = (walletId: string) => { 37 | return useQuery({ 38 | queryKey: ["listTransactions", walletId], 39 | queryFn: () => transactionsHelper(walletId), 40 | }); 41 | }; 42 | 43 | // Get transaction details query 44 | const transactionHelper = async (transactionId: string) => { 45 | const response = await axios.get<{ 46 | transaction: Transaction; 47 | }>(`/transactions/${transactionId}`); 48 | 49 | return response.data.transaction; 50 | }; 51 | 52 | export const useTransactionQuery = (transactionId: string) => { 53 | return useQuery({ 54 | queryKey: ["getTransaction", transactionId], 55 | queryFn: () => transactionHelper(transactionId), 56 | }); 57 | }; 58 | 59 | // Estimate Transfer Fee 60 | const estimateFeeHelper = async (input: EstimateFeeInput) => { 61 | const response = await axios.post( 62 | "/transactions/transfer/estimateFee", 63 | input 64 | ); 65 | 66 | return response.data; 67 | }; 68 | 69 | export const useEstimateFeeMutation = () => useMutation(estimateFeeHelper); 70 | 71 | // Validate address for transaction mutation 72 | const validateAddressMutationHelper = async ({ 73 | address, 74 | blockchain, 75 | }: { 76 | address: string; 77 | blockchain: string; 78 | }) => { 79 | const { data } = await axios.post<{}, { data: { isValid: boolean } }>( 80 | "/transactions/validateAddress", 81 | { 82 | address, 83 | blockchain, 84 | } 85 | ); 86 | 87 | return data; 88 | }; 89 | 90 | export const useValidateAddressMutation = () => 91 | useMutation(validateAddressMutationHelper); 92 | 93 | // Create transaction mutation 94 | const createTransferHelper = async (bodyParams: { 95 | destinationAddress: string; 96 | tokenId: string; 97 | walletId: string; 98 | amounts: string[]; 99 | feeLevel: "LOW" | "MEDIUM" | "HIGH"; 100 | }) => { 101 | const response = await axios.post<{ challengeId: string }>( 102 | "/transactions/transfer", 103 | bodyParams 104 | ); 105 | 106 | return response.data; 107 | }; 108 | 109 | export const useCreateTransferMutation = () => 110 | useMutation(createTransferHelper); 111 | -------------------------------------------------------------------------------- /app/axios/users.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | "use client"; 18 | import { axios } from "@/app/axios"; 19 | import { useMutation } from "react-query"; 20 | 21 | export const restorePinHelper = async () => { 22 | const response = await axios.post<{ challengeId: string }>( 23 | "/users/pin/restore", 24 | ); 25 | 26 | return response.data.challengeId; 27 | }; 28 | 29 | export const useRestorePinMutation = () => { 30 | return useMutation({ 31 | mutationKey: ["restorePin"], 32 | mutationFn: () => restorePinHelper(), 33 | }); 34 | }; 35 | -------------------------------------------------------------------------------- /app/axios/wallets.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | "use client"; 18 | import { axios } from "@/app/axios"; 19 | import { useMutation, useQuery } from "react-query"; 20 | import { 21 | BlockchainEnum, 22 | TokenBalance, 23 | Wallet, 24 | WalletBalancesInput, 25 | WalletsInput, 26 | } from "../shared/types"; 27 | 28 | const walletBalanceHelper = async ( 29 | id: string, 30 | params?: WalletBalancesInput, 31 | ) => { 32 | return axios.get<{ 33 | tokenBalances: TokenBalance[]; 34 | }>(`/wallets/${id}/balances`, { 35 | params: { 36 | name: params?.name, 37 | }, 38 | }); 39 | }; 40 | 41 | export const useWalletBalances = (id: string, params?: WalletBalancesInput) => { 42 | return useQuery({ 43 | queryKey: ["getWalletBalance", id, params], 44 | queryFn: () => walletBalanceHelper(id, params), 45 | }); 46 | }; 47 | 48 | const walletsHelper = async (params?: WalletsInput) => { 49 | return axios.get<{ 50 | wallets: Wallet[]; 51 | }>(`/wallets`, { 52 | params: { 53 | ...params, 54 | }, 55 | }); 56 | }; 57 | 58 | export const useWallets = (params?: WalletsInput, refetchInterval?: any) => { 59 | return useQuery({ 60 | queryKey: ["getWallets", params], 61 | queryFn: () => walletsHelper(params), 62 | refetchInterval: refetchInterval, 63 | }); 64 | }; 65 | 66 | const walletHelper = async (id: string) => { 67 | return axios.get<{ 68 | wallet: Wallet; 69 | }>(`/wallets/${id}`); 70 | }; 71 | 72 | export const useWallet = (id: string) => { 73 | return useQuery({ 74 | queryKey: ["getWallet", id], 75 | queryFn: () => walletHelper(id), 76 | }); 77 | }; 78 | 79 | export const useCreateWallet = () => { 80 | return useMutation((input: { blockchain: BlockchainEnum }) => { 81 | return axios.post(`/wallets`, input); 82 | }); 83 | }; 84 | -------------------------------------------------------------------------------- /app/components/Authentication/AuthenticationForm.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | "use client"; 18 | import { useForm, SubmitHandler } from "react-hook-form"; 19 | import { useW3sContext } from "../Providers/W3sProvider"; 20 | import { useRouter } from "next/navigation"; 21 | import { useEffect, useState } from "react"; 22 | 23 | import Button from "@mui/joy/Button"; 24 | import { signIn, useSession } from "next-auth/react"; 25 | import { TextField } from "@/app/components/TextField"; 26 | import { EyeIcon, EyeSlashIcon } from "@heroicons/react/16/solid"; 27 | import { IconButton, Typography } from "@mui/joy"; 28 | import { yupResolver } from "@hookform/resolvers/yup"; 29 | import * as yup from "yup"; 30 | import { Content } from ".."; 31 | 32 | const formSchema = yup.object({ 33 | email: yup 34 | .string() 35 | .email("Please enter a valid email.") 36 | .required("Email required"), 37 | password: yup 38 | .string() 39 | .min(8, "Password must be at least 8 characters") 40 | .required("Password required"), 41 | }); 42 | 43 | type FormInputs = yup.InferType; 44 | 45 | interface AuthenticationFormProps { 46 | /** 47 | * Is the form a sign in form 48 | */ 49 | isSignIn?: boolean; 50 | } 51 | 52 | export const AuthenticationForm: React.FC = ({ 53 | isSignIn = true, 54 | }) => { 55 | const { register, handleSubmit, formState } = useForm({ 56 | resolver: yupResolver(formSchema), 57 | }); 58 | const [loading, setLoading] = useState(false); 59 | const [isMasked, setIsMasked] = useState(true); 60 | const [formMessage, setFormMessage] = useState(undefined); 61 | const [redirect, setRedirect] = useState(false); 62 | const router = useRouter(); 63 | const { client } = useW3sContext(); 64 | const { data: session } = useSession(); 65 | 66 | useEffect(() => { 67 | if (redirect && client && session) { 68 | if (session.user.challengeId) { 69 | client.execute(session.user.challengeId, (error, result) => { 70 | if (error) { 71 | setFormMessage("An error occurred on PIN Setup. Please try again."); 72 | } else if (result) { 73 | // result will be undefined if popup is closed 74 | // only navigate to wallets if PIN setup complete 75 | router.push("/wallets"); 76 | } 77 | }); 78 | } else { 79 | router.push("/wallets"); 80 | } 81 | setLoading(false); 82 | } 83 | }, [redirect, session, session?.user, client, router]); 84 | 85 | const onSubmit: SubmitHandler = async (data) => { 86 | setLoading(true); 87 | if (!isSignIn) { 88 | const res = await signIn("SignUp", { 89 | email: data.email, 90 | password: data.password, 91 | redirect: false, 92 | }); 93 | 94 | if (res?.ok) { 95 | return setRedirect(true); 96 | } else if (res?.error) { 97 | setFormMessage(res.error); 98 | } else { 99 | setFormMessage("An error occurred on Sign Up. Please try again."); 100 | } 101 | setLoading(false); 102 | } else { 103 | const res = await signIn("SignIn", { 104 | email: data.email, 105 | password: data.password, 106 | redirect: false, 107 | }); 108 | 109 | if (res?.ok) { 110 | return setRedirect(true); 111 | } else { 112 | setFormMessage("Invalid Credentials."); 113 | } 114 | setLoading(false); 115 | } 116 | }; 117 | return ( 118 | 119 |

120 | {isSignIn ? "Sign In" : "Sign Up"} 121 |

122 | 123 |
124 |
125 | 133 | setIsMasked((f) => !f)}> 141 | {isMasked ? : } 142 | 143 | } 144 | {...register("password")} 145 | /> 146 | 155 |

{formMessage ? formMessage : ""}

156 |
157 |
158 | 159 | 160 | {isSignIn ? "Don't have an account?" : "Already have an account?"} 161 | 162 | 163 | 172 |
173 | ); 174 | }; 175 | -------------------------------------------------------------------------------- /app/components/Buttons/BackButton.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import { ArrowLeftIcon } from "@heroicons/react/16/solid"; 18 | 19 | import { IconButton, Typography } from "@mui/joy"; 20 | 21 | export const BackButton = ({ 22 | children, 23 | onClick, 24 | }: { 25 | children: React.ReactNode; 26 | onClick: () => void; 27 | }) => { 28 | return ( 29 |
30 | 31 | 32 | 33 | 34 | {children} 35 | 36 |
37 | ); 38 | }; 39 | -------------------------------------------------------------------------------- /app/components/Buttons/CopyButton.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | "use client"; 18 | import { useCallback, useEffect, useRef, useState } from "react"; 19 | 20 | import Button, { ButtonProps } from "@mui/joy/Button"; 21 | import { CheckIcon } from "@heroicons/react/16/solid"; 22 | import { DocumentDuplicateIcon } from "@heroicons/react/24/outline"; 23 | 24 | import { IconButton } from "@mui/joy"; 25 | 26 | interface CopyButtonProps { 27 | copyValue: string; 28 | copyLabel?: string; 29 | variant?: ButtonProps['variant']; 30 | } 31 | 32 | const COPY_CLIPBOARD_RESET_INTERVAL = 3000; 33 | export const CopyButton: React.FC = ({ 34 | copyLabel, 35 | copyValue, 36 | variant = 'plain', 37 | }) => { 38 | const [copied, setCopied] = useState(false); 39 | const setTimerRef = useRef(); 40 | 41 | useEffect(() => { 42 | // Clear the interval when the component unmounts 43 | return () => { 44 | if (setTimerRef.current) { 45 | clearTimeout(setTimerRef.current); 46 | } 47 | }; 48 | }, []); 49 | 50 | const handleCopyToClipboard = useCallback(async () => { 51 | await navigator.clipboard.writeText(copyValue); 52 | setCopied(true); 53 | if (setTimerRef.current) { 54 | clearTimeout(setTimerRef.current); 55 | } 56 | setTimerRef.current = setTimeout(() => { 57 | setCopied(false); 58 | }, COPY_CLIPBOARD_RESET_INTERVAL); 59 | }, [copyValue]); 60 | 61 | return copyLabel ? ( 62 | 72 | ) : ( 73 | 74 | {copied ? : } 75 | 76 | ); 77 | }; 78 | -------------------------------------------------------------------------------- /app/components/Buttons/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | export * from "./BackButton"; 18 | export * from "./CopyButton"; 19 | -------------------------------------------------------------------------------- /app/components/Cards/TokenCard.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import { CardContent, Typography } from "@mui/joy"; 18 | import Card from "@mui/joy/Card"; 19 | import { ExclamationCircleIcon } from "@heroicons/react/16/solid"; 20 | 21 | import { tokenHelper } from "@/app/shared/utils"; 22 | import Image from "next/image"; 23 | import { Token } from "@/app/shared/types"; 24 | 25 | interface TokenCardProps { 26 | token?: Token; 27 | amount: string; 28 | onClick?: () => void; 29 | } 30 | 31 | /** 32 | * Token balance card. 33 | */ 34 | export const TokenCard: React.FC = ({ 35 | token, 36 | amount, 37 | onClick, 38 | }) => { 39 | const tokenMeta = tokenHelper(token?.name); 40 | 41 | return ( 42 | 46 | 47 | {tokenMeta.svg !== "" ? ( 48 | token 55 | ) : ( 56 | 57 | )} 58 |
59 | {tokenMeta.name} 60 | 61 | {`${amount} ${tokenMeta.name}`} 62 |
63 |
64 |
65 | ); 66 | }; 67 | -------------------------------------------------------------------------------- /app/components/Content.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import React from "react"; 18 | 19 | export const Content = ({ children }: { children: React.ReactNode }) => { 20 | return ( 21 |
22 | {children} 23 |
24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /app/components/Footer.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import { Typography } from "@mui/joy"; 18 | import Link from "next/link"; 19 | 20 | export const Footer = () => ( 21 |
22 | 23 | © 2024 Circle Technology Services, LLC. All rights reserved. 24 | 25 | 26 | 27 | 32 | Privacy Policy 33 | 34 | 35 | 40 | Acceptable Use 41 | 42 | 43 | 48 | Cookie Policy 49 | 50 | 51 |
52 | ); 53 | -------------------------------------------------------------------------------- /app/components/Loading/LoadingWrapper.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import { ReactNode } from "react"; 18 | import { Box, CircularProgress } from "@mui/joy"; 19 | 20 | interface LoadingWrapper { 21 | isLoading: boolean; 22 | children: ReactNode; 23 | } 24 | 25 | export const LoadingWrapper: React.FC = ({ 26 | isLoading, 27 | children, 28 | }) => { 29 | return ( 30 | <> 31 | {isLoading ? ( 32 | 33 | 34 | 35 | ) : ( 36 | children 37 | )} 38 | 39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /app/components/Providers/ClientProviders.tsx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Circle Technologies, LLC. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | "use client"; 17 | 18 | import createCache from "@emotion/cache"; 19 | import { CacheProvider } from "@emotion/react"; 20 | import { CssVarsProvider, CssBaseline } from "@mui/joy"; 21 | import { SessionProvider } from "next-auth/react"; 22 | import { useServerInsertedHTML } from "next/navigation"; 23 | import React from "react"; 24 | import { QueryClient, QueryClientProvider } from "react-query"; 25 | import { W3sProvider } from "./W3sProvider"; 26 | 27 | const queryClient = new QueryClient(); 28 | 29 | export const ClientProviders = ({ 30 | children, 31 | }: { 32 | children: React.ReactNode; 33 | }) => { 34 | return ( 35 | 36 | 37 | 38 | {children} 39 | 40 | 41 | 42 | ); 43 | }; 44 | 45 | // copied from joy-ui docs. 46 | // https://mui.com/joy-ui/integrations/next-js-app-router/#using-joy-ui-with-the-app-router 47 | 48 | function ThemeRegistry(props: any) { 49 | const { options, children } = props; 50 | 51 | const [{ cache, flush }] = React.useState(() => { 52 | const cache = createCache(options); 53 | cache.compat = true; 54 | const prevInsert = cache.insert; 55 | let inserted: string[] = []; 56 | cache.insert = (...args) => { 57 | const serialized = args[1]; 58 | if (cache.inserted[serialized.name] === undefined) { 59 | inserted.push(serialized.name); 60 | } 61 | return prevInsert(...args); 62 | }; 63 | const flush = () => { 64 | const prevInserted = inserted; 65 | inserted = []; 66 | return prevInserted; 67 | }; 68 | return { cache, flush }; 69 | }); 70 | 71 | useServerInsertedHTML(() => { 72 | const names = flush(); 73 | if (names.length === 0) { 74 | return null; 75 | } 76 | let styles = ""; 77 | for (const name of names) { 78 | styles += cache.inserted[name]; 79 | } 80 | return ( 81 |