├── .env.example ├── .eslintrc.cjs ├── .firebaserc ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── build-and-release.yml │ ├── firebase-hosting-merge.yml │ └── firebase-hosting-pull-request.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── SUPPORT.md ├── firebase.json ├── index.html ├── package-lock.json ├── package.json ├── patches └── @cmdcode+tapscript+1.4.3.patch ├── public ├── background.js ├── chromestore.png ├── content.js ├── inject.js ├── logo.png ├── logo.svg └── manifest.json ├── src ├── app │ ├── app-context.tsx │ ├── app.hook.ts │ ├── app.tsx │ ├── index.ts │ └── settings.ts ├── bitcoin │ ├── fees.ts │ ├── helpers.ts │ ├── lib │ │ └── bitcoin-lib.ts │ ├── node.ts │ ├── wallet-storage.ts │ └── wallet.ts ├── components │ ├── app-layout.tsx │ ├── dollar-balance.tsx │ ├── icon.tsx │ ├── index.ts │ ├── layout.tsx │ └── set-fees.tsx ├── constants │ └── constants.ts ├── env.d.ts ├── hooks │ ├── address.hook.ts │ ├── create-wallet.hook.ts │ ├── get-balances.hook.ts │ ├── index.ts │ ├── restore-wallet.hook.ts │ ├── safe-balances.hook.ts │ ├── send-sats.hook.ts │ ├── send-tokens.hook.ts │ └── show-transactions.hook.ts ├── main.tsx ├── router │ ├── index.ts │ ├── route-path.ts │ ├── router-provider.tsx │ └── router.tsx ├── screens │ ├── addresses.tsx │ ├── balances.tsx │ ├── confirm-transaction.tsx │ ├── connect-wallet.tsx │ ├── create-wallet.tsx │ ├── decode-and-sign-psbt.tsx │ ├── explore.tsx │ ├── index.ts │ ├── mnemonic.tsx │ ├── modals │ │ ├── reset-storage.tsx │ │ ├── show-address.tsx │ │ └── show-mnemonic.tsx │ ├── password.tsx │ ├── restore-wallet.tsx │ ├── send.tsx │ ├── settings.tsx │ ├── sign-message.tsx │ ├── switch-network.tsx │ └── verify-sign.tsx ├── theme │ ├── index.ts │ ├── theme-constants.ts │ ├── theme-provider.tsx │ └── theme.ts ├── transfer │ ├── get-ordinals-unspents.ts │ ├── get-pipe-unspents.ts │ ├── get-unspents.ts │ ├── prepare-transfer-pipe.ts │ ├── prepare-transfer-sats.ts │ ├── select-all-ordinals-unspents.ts │ ├── select-all-pipe-unspents.ts │ ├── select-all-unspents.ts │ ├── select-unspents.ts │ ├── transfer-pipe.ts │ └── transfer-sats.ts ├── utils │ ├── bitcoin-price.ts │ ├── calculate-fee.ts │ ├── clean-float.ts │ ├── crypto.ts │ ├── fingerprint.ts │ ├── hash-to-string.ts │ ├── hex-to-bytes.ts │ ├── sats-to-btc.ts │ ├── sats-to-dollars.ts │ └── truncate-in-middle.ts └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.env.example: -------------------------------------------------------------------------------- 1 | VITE_SERVER_HOST="" 2 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "inspip-wallet" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: [] 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **System (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Wallet version [e.g. 22] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/build-and-release.yml: -------------------------------------------------------------------------------- 1 | name: Build and Release 2 | 3 | on: 4 | # push: 5 | # tags: 6 | # - 'v*' 7 | push: 8 | branches: 9 | - "main" 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: write 16 | 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v3 20 | 21 | - name: Setup Node.js 22 | uses: actions/setup-node@v3 23 | with: 24 | node-version: '19' 25 | 26 | - name: Install dependencies 27 | run: npm install 28 | 29 | - name: Build project 30 | run: npm run build 31 | env: 32 | VITE_SERVER_HOST: ${{ secrets.VITE_SERVER_HOST }} 33 | 34 | - name: Package /dist directory 35 | run: zip -r dist.zip dist/ 36 | 37 | - name: Create Release 38 | uses: ncipollo/release-action@v1 39 | with: 40 | artifacts: dist.zip 41 | tag: "v0.1.12" 42 | allowUpdates: true 43 | artifactContentType: application/zip 44 | 45 | - name: Upload Release Asset 46 | uses: softprops/action-gh-release@v1 47 | with: 48 | files: dist.zip 49 | tag_name: "v0.1.12" 50 | -------------------------------------------------------------------------------- /.github/workflows/firebase-hosting-merge.yml: -------------------------------------------------------------------------------- 1 | # This file was auto-generated by the Firebase CLI 2 | # https://github.com/firebase/firebase-tools 3 | 4 | name: Deploy to Firebase Hosting on merge 5 | 'on': 6 | push: 7 | branches: 8 | - main 9 | jobs: 10 | build_and_deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - run: npm ci && npm run build 15 | - uses: FirebaseExtended/action-hosting-deploy@v0 16 | with: 17 | repoToken: '${{ secrets.GITHUB_TOKEN }}' 18 | firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_INSPIP_WALLET }}' 19 | channelId: live 20 | projectId: inspip-wallet 21 | -------------------------------------------------------------------------------- /.github/workflows/firebase-hosting-pull-request.yml: -------------------------------------------------------------------------------- 1 | # This file was auto-generated by the Firebase CLI 2 | # https://github.com/firebase/firebase-tools 3 | 4 | name: Deploy to Firebase Hosting on PR 5 | 'on': pull_request 6 | jobs: 7 | build_and_preview: 8 | if: '${{ github.event.pull_request.head.repo.full_name == github.repository }}' 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - run: npm ci && npm run build 13 | - uses: FirebaseExtended/action-hosting-deploy@v0 14 | with: 15 | repoToken: '${{ secrets.GITHUB_TOKEN }}' 16 | firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_INSPIP_WALLET }}' 17 | projectId: inspip-wallet 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | .yarn.lock 5 | .env 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | pnpm-debug.log* 10 | lerna-debug.log* 11 | 12 | node_modules 13 | dist 14 | dist-ssr 15 | *.local 16 | # Editor directories and files 17 | .vscode/* 18 | !.vscode/extensions.json 19 | .idea 20 | .DS_Store 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | . 27 | DS_Store 28 | .firebase 29 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | [version] - 0.1.12 2 | 3 | - [x] Wif fix 4 | - [x] Injection improvements 5 | 6 | [version] - 0.1.11 7 | 8 | - [x] Chrome extension size fix 9 | 10 | [version] - 0.1.10 11 | 12 | - [x] Protection for ordinals 13 | - [x] Transfer bitcoin refactor 14 | - [x] Transfer pipe refactor 15 | - [x] Testnet improvements 16 | 17 | [version] - 0.1.9 18 | 19 | - [x] UX & UI improvements 20 | - [x] Fix Sending Transactions vin issue 21 | 22 | [version] - 0.1.8 23 | 24 | - [x] Browser Injection: 25 | - [x] Connect 26 | - [x] Send bitcoin 27 | - [x] Hot Reload for Extension 28 | - [x] Mnemo click to copy 29 | - [x] Editor settings 30 | - [x] Wallet Settings 31 | - [x] Switch Testnet 32 | - [x] Fee calculator with vsize -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Business Source License 1.1 2 | 3 | License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved. 4 | "Business Source License" is a trademark of MariaDB Corporation Ab. 5 | 6 | ----------------------------------------------------------------------------- 7 | 8 | Parameters 9 | 10 | Licensor: Inscrib3 11 | 12 | Licensed Work: Inscrib3 13 | The Licensed Work is (c) 2023 Inscrib3 14 | 15 | Additional Use Grant: Research and auditing 16 | 17 | Change Date: The earlier of 2025-01-01 18 | 19 | Change License: GNU General Public License v2.0 or later 20 | 21 | ----------------------------------------------------------------------------- 22 | 23 | Terms 24 | 25 | The Licensor hereby grants you the right to copy, modify, create derivative 26 | works, redistribute, and make non-production use of the Licensed Work. The 27 | Licensor may make an Additional Use Grant, above, permitting limited 28 | production use. 29 | 30 | Effective on the Change Date, or the fourth anniversary of the first publicly 31 | available distribution of a specific version of the Licensed Work under this 32 | License, whichever comes first, the Licensor hereby grants you rights under 33 | the terms of the Change License, and the rights granted in the paragraph 34 | above terminate. 35 | 36 | If your use of the Licensed Work does not comply with the requirements 37 | currently in effect as described in this License, you must purchase a 38 | commercial license from the Licensor, its affiliated entities, or authorized 39 | resellers, or you must refrain from using the Licensed Work. 40 | 41 | All copies of the original and modified Licensed Work, and derivative works 42 | of the Licensed Work, are subject to this License. This License applies 43 | separately for each version of the Licensed Work and the Change Date may vary 44 | for each version of the Licensed Work released by Licensor. 45 | 46 | You must conspicuously display this License on each original or modified copy 47 | of the Licensed Work. If you receive the Licensed Work in original or 48 | modified form from a third party, the terms and conditions set forth in this 49 | License apply to your use of that work. 50 | 51 | Any use of the Licensed Work in violation of this License will automatically 52 | terminate your rights under this License for the current and all other 53 | versions of the Licensed Work. 54 | 55 | This License does not grant you any right in any trademark or logo of 56 | Licensor or its affiliates (provided that you may use a trademark or logo of 57 | Licensor as expressly required by this License). 58 | 59 | TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON 60 | AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, 61 | EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF 62 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND 63 | TITLE. 64 | 65 | MariaDB hereby grants you permission to use this License's text to license 66 | your works, and to refer to it using the trademark "Business Source License", 67 | as long as you comply with the Covenants of Licensor below. 68 | 69 | ----------------------------------------------------------------------------- 70 | 71 | Covenants of Licensor 72 | 73 | In consideration of the right to use this License’s text and the "Business 74 | Source License" name and trademark, Licensor covenants to MariaDB, and to all 75 | other recipients of the licensed work to be provided by Licensor: 76 | 77 | 1. To specify as the Change License the GPL Version 2.0 or any later version, 78 | or a license that is compatible with GPL Version 2.0 or a later version, 79 | where "compatible" means that software provided under the Change License can 80 | be included in a program with software provided under GPL Version 2.0 or a 81 | later version. Licensor may specify additional Change Licenses without 82 | limitation. 83 | 84 | 2. To either: (a) specify an additional grant of rights to use that does not 85 | impose any additional restriction on the right granted in this License, as 86 | the Additional Use Grant; or (b) insert the text "None". 87 | 88 | 3. To specify a Change Date. 89 | 90 | 4. Not to modify this License in any other way. 91 | 92 | ----------------------------------------------------------------------------- 93 | 94 | Notice 95 | 96 | The Business Source License (this document, or the "License") is not an Open 97 | Source license. However, the Licensed Work will eventually be made available 98 | under an Open Source License, as stated in this License. 99 | 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Inspip | Pipe Wallet Extension 2 | 3 | ## Experimental Public Beta: Use at your own risk, no refunds, no warranties. 4 | 5 | Easily manage and interact with PIPE tokens on the Bitcoin network right from your browser. The Pipe Wallet extension provides a simple interface to *Deploy*, *Mint*, and *Transfer* tokens adhering to the PIPE protocol specifications. 6 | 7 | ## Features 8 | - Fully non-custodial: **you control your private keys** and they never leave your device. Don't screw up, we cannot recover lost keys! 9 | - Seamless token management: Deploy, Mint, Transfer. 10 | - Securely sign transactions. 11 | - View transaction history and token balances. 12 | 13 | ## Installation 14 | Make sure to use Chrome, Brave or Chromium browser. Firefox and Safari are not supported at this time. 15 | 16 | 1. Download the extension from the *Release* page 17 | 2. Go to `chrome://extensions/` or `brave://extensions/` 18 | 3. Click *Load unpacked* and select the downloaded folder 19 | 20 | ## Support 21 | For support or any inquiries, please visit our [Discord](https://discord.gg/gpFGS4UJ5f). 22 | 23 | ## How to Contribute 24 | Any PR is welcome. You can also tip us some spare sats if you like the project. 25 | Your donations will contribute to the ongoing development, maintenance, and customer support. Find more [here](SUPPORT.md). 26 | 27 | ## PIPE Protocol Overview 28 | [PIPE](https://github.com/BennyTheDev/pipe-specs) is a Bitcoin-native token protocol with three main functions: 29 | 30 | **Deploy**, **Mint**, and **Transfer** (DMT): 31 | 32 | - **Deploy**: Initiates a new token with defined attributes like ticker name, maximum supply, and minting limits. 33 | - **Mint**: Allows the creation of new tokens within the defined limits set during deployment. 34 | - **Transfer**: Facilitates the sending of tokens to selected recipients. 35 | 36 | The PIPE protocol, by introducing a structured way to deploy, mint, and transfer tokens on the Bitcoin network, provides a framework for tokenized assets and applications which isn't natively supported by Bitcoin. This potentially allows for a variety of decentralized applications (dApps), tokenized assets, and smart contract-like behaviors on Bitcoin, which are features more commonly associated with platforms like Ethereum. 37 | In essence, it can bring a new level of functionality to Bitcoin while still utilizing Bitcoin's robust and secure blockchain. 38 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support Pipe Wallet Development 2 | We're committed to making the Pipe Wallet the best it can be. Your contributions can help us achieve this goal. 3 | 4 | ## How to Contribute 5 | Your donations will contribute to the ongoing development, maintenance, and customer support. 6 | 7 | **Bitcoin Address for Donations**: `3GbeGZnnricNemsneqhjEWa9r2JbBRzdyu` 8 | 9 | Any amount is appreciated. Thank you for your support! 10 | 11 | ## Contact 12 | For more information on how your donation will be used, feel free to contact on [Discord](https://discord.gg/gpFGS4UJ5f) or email [inscrib3@proton.me](mailto:inscrib3@proton.me). 13 | 14 | ## Supporters 15 | We are grateful to these wonderful individuals who helped support the Pipe wallet project: 16 | 17 | - Your name here... 18 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "dist", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | Inscrib3 | Pipe Wallet Extension 13 | 14 | 15 | 16 | 17 | 32 | 33 | 34 |
35 | 36 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inspip", 3 | "private": true, 4 | "version": "0.1.12", 5 | "type": "module", 6 | "license": "BUSL-1.1", 7 | "scripts": { 8 | "dev": "vite", 9 | "build": "tsc && vite build", 10 | "build:watch": "vite build --watch", 11 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 12 | "preview": "vite preview" 13 | }, 14 | "dependencies": { 15 | "@bitcoin-js/tiny-secp256k1-asmjs": "^2.2.3", 16 | "@cmdcode/tapscript": "^1.4.3", 17 | "@types/chrome": "^0.0.258", 18 | "bip32": "^4.0.0", 19 | "bip322-js": "^1.1.0", 20 | "bip39": "^3.1.0", 21 | "bitcoinjs-lib": "^6.1.5", 22 | "bs58": "^5.0.0", 23 | "buffer": "^6.0.3", 24 | "crypto-js": "^4.1.1", 25 | "ecpair": "^2.1.0", 26 | "firebase": "^10.8.0", 27 | "grommet": "^2.33.2", 28 | "grommet-icons": "^4.11.0", 29 | "localforage": "^1.10.0", 30 | "match-sorter": "^6.3.1", 31 | "murmurhash-js": "^1.0.0", 32 | "qrcode.react": "^3.1.0", 33 | "react": "^18.2.0", 34 | "react-dom": "^18.2.0", 35 | "react-router-dom": "^6.16.0", 36 | "sort-by": "^1.2.0", 37 | "styled-components": "^5.1.0", 38 | "typescript": "^5.2.2" 39 | }, 40 | "devDependencies": { 41 | "@rollup/plugin-inject": "^5.0.5", 42 | "@types/crypto-js": "^4.1.3", 43 | "@types/murmurhash-js": "^1.0.5", 44 | "@types/node": "^20.8.9", 45 | "@types/react": "^18.2.15", 46 | "@types/react-dom": "^18.2.7", 47 | "@typescript-eslint/eslint-plugin": "^6.0.0", 48 | "@typescript-eslint/parser": "^6.0.0", 49 | "@vitejs/plugin-react": "^4.0.3", 50 | "eslint": "^8.45.0", 51 | "eslint-plugin-react-hooks": "^4.6.0", 52 | "eslint-plugin-react-refresh": "^0.4.3", 53 | "hot-reload-extension-vite": "^1.0.13", 54 | "patch-package": "^7.0.0", 55 | "vite": "^4.4.5", 56 | "vite-plugin-node-polyfills": "^0.15.0" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /patches/@cmdcode+tapscript+1.4.3.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/@cmdcode/tapscript/package.json b/node_modules/@cmdcode/tapscript/package.json 2 | index d3b3491..2d5ed53 100644 3 | --- a/node_modules/@cmdcode/tapscript/package.json 4 | +++ b/node_modules/@cmdcode/tapscript/package.json 5 | @@ -15,7 +15,8 @@ 6 | "require": { 7 | "types": "./dist/types/index.d.ts", 8 | "default": "./dist/main.cjs" 9 | - } 10 | + }, 11 | + "types": "./dist/types/index.d.ts" 12 | } 13 | }, 14 | "scripts": { 15 | -------------------------------------------------------------------------------- /public/background.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-undef 2 | chrome.runtime.onMessage.addListener(async (request) => { 3 | if (typeof request.message !== 'string') { 4 | if (request.message.action === 'SendBitcoin') { 5 | const params = request.message.params; 6 | // eslint-disable-next-line no-undef 7 | chrome.windows.create({ 8 | type: 'popup', 9 | url: `index.html#?action=SendBitcoin&feerate=${params.feerate}&toAddress=${params.toAddress}&satoshi=${params.satoshi}`, 10 | width: 400, 11 | height: 600, 12 | }); 13 | } 14 | if (request.message.action === 'SendTokens') { 15 | const params = request.message.params; 16 | // eslint-disable-next-line no-undef 17 | chrome.windows.create({ 18 | type: 'popup', 19 | url: `index.html#?action=SendTokens&feerate=${params.feerate}&toAddress=${params.toAddress}&amount=${params.amount}&id=${params.id}&ticker=${params.ticker}`, 20 | width: 400, 21 | height: 600, 22 | }); 23 | } 24 | if (request.message.action === 'SignPsbt') { 25 | const params = request.message.params; 26 | // eslint-disable-next-line no-undef 27 | chrome.windows.create({ 28 | type: 'popup', 29 | url: `index.html#?action=SignPsbt&psbt=${params.psbt}&toSignInputs=${params.toSignInputs}&autoFinalized=${params.autoFinalized}`, 30 | width: 400, 31 | height: 600, 32 | }); 33 | } 34 | if (request.message.action === 'SignMessage') { 35 | const params = request.message.params; 36 | // eslint-disable-next-line no-undef 37 | chrome.windows.create({ 38 | type: 'popup', 39 | url: `index.html#?action=SignMessage&msg=${params.msg}`, 40 | width: 400, 41 | height: 600, 42 | }); 43 | } 44 | if (request.message.action === 'VerifySign') { 45 | const params = request.message.params; 46 | // eslint-disable-next-line no-undef 47 | chrome.windows.create({ 48 | type: 'popup', 49 | url: `index.html#?action=VerifySign&msg=${params.msg}&signature=${params.signature}`, 50 | width: 400, 51 | height: 600, 52 | }); 53 | } 54 | if (request.message.action === 'ConnectWallet') { 55 | // eslint-disable-next-line no-undef 56 | chrome.windows.create({ 57 | type: 'popup', 58 | url: `index.html#?action=ConnectWallet`, 59 | width: 400, 60 | height: 600, 61 | }); 62 | } 63 | } else { 64 | if (request.message.includes('ReturnSendTokens')) { 65 | // eslint-disable-next-line no-undef 66 | chrome.tabs.query({ active: true }, function(tabs) { 67 | // eslint-disable-next-line no-undef 68 | chrome.tabs.sendMessage(tabs[0].id, { message: request.message }); 69 | }); 70 | } 71 | if (request.message.includes('ReturnConnectWalletInfo')) { 72 | // eslint-disable-next-line no-undef 73 | chrome.tabs.query({ active: true }, function(tabs) { 74 | // eslint-disable-next-line no-undef 75 | chrome.tabs.sendMessage(tabs[0].id, { message: request.message }); 76 | }); 77 | } 78 | if (request.message.includes('ClientRejectConnectWalletInfo')) { 79 | // eslint-disable-next-line no-undef 80 | chrome.tabs.query({ active: true }, function(tabs) { 81 | // eslint-disable-next-line no-undef 82 | chrome.tabs.sendMessage(tabs[0].id, { message: request.message }); 83 | }); 84 | } 85 | if (request.message.includes('ReturnSendBitcoin')) { 86 | // eslint-disable-next-line no-undef 87 | chrome.tabs.query({ active: true }, function(tabs) { 88 | // eslint-disable-next-line no-undef 89 | chrome.tabs.sendMessage(tabs[0].id, { message: request.message }); 90 | }); 91 | } 92 | if (request.message.includes('ReturnSignPsbt')) { 93 | // eslint-disable-next-line no-undef 94 | chrome.tabs.query({ active: true }, function(tabs) { 95 | // eslint-disable-next-line no-undef 96 | chrome.tabs.sendMessage(tabs[0].id, { message: request.message }); 97 | }); 98 | } 99 | if (request.message.includes('ReturnErrorOnSignPsbt')) { 100 | // eslint-disable-next-line no-undef 101 | chrome.tabs.query({ active: true }, function(tabs) { 102 | // eslint-disable-next-line no-undef 103 | chrome.tabs.sendMessage(tabs[0].id, { message: request.message }); 104 | }); 105 | } 106 | if (request.message.includes('ClientRejectSignPsbt')) { 107 | // eslint-disable-next-line no-undef 108 | chrome.tabs.query({ active: true }, function(tabs) { 109 | // eslint-disable-next-line no-undef 110 | chrome.tabs.sendMessage(tabs[0].id, { message: request.message }); 111 | }); 112 | } 113 | if (request.message.includes('ReturnSignMessage')) { 114 | // eslint-disable-next-line no-undef 115 | chrome.tabs.query({ active: true }, function(tabs) { 116 | // eslint-disable-next-line no-undef 117 | chrome.tabs.sendMessage(tabs[0].id, { message: request.message }); 118 | }); 119 | } 120 | if (request.message.includes('ReturnErrorOnSignMessage')) { 121 | // eslint-disable-next-line no-undef 122 | chrome.tabs.query({ active: true }, function(tabs) { 123 | // eslint-disable-next-line no-undef 124 | chrome.tabs.sendMessage(tabs[0].id, { message: request.message }); 125 | }); 126 | } 127 | if (request.message.includes('ClientRejectSignMessage')) { 128 | // eslint-disable-next-line no-undef 129 | chrome.tabs.query({ active: true }, function(tabs) { 130 | // eslint-disable-next-line no-undef 131 | chrome.tabs.sendMessage(tabs[0].id, { message: request.message }); 132 | }); 133 | } 134 | if (request.message.includes('ReturnVerifySign')) { 135 | // eslint-disable-next-line no-undef 136 | chrome.tabs.query({ active: true }, function(tabs) { 137 | // eslint-disable-next-line no-undef 138 | chrome.tabs.sendMessage(tabs[0].id, { message: request.message }); 139 | }); 140 | } 141 | if (request.message.includes('ReturnErrorOnVerifySign')) { 142 | // eslint-disable-next-line no-undef 143 | chrome.tabs.query({ active: true }, function(tabs) { 144 | // eslint-disable-next-line no-undef 145 | chrome.tabs.sendMessage(tabs[0].id, { message: request.message }); 146 | }); 147 | } 148 | } 149 | }); -------------------------------------------------------------------------------- /public/chromestore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inscrib3/inspip/3671b68eb56adc743fe3f4498b3306932b5340da/public/chromestore.png -------------------------------------------------------------------------------- /public/content.js: -------------------------------------------------------------------------------- 1 | var scriptElement = document.createElement('script'); 2 | 3 | // eslint-disable-next-line no-undef 4 | scriptElement.src = chrome.runtime.getURL('inject.js'); 5 | 6 | document.head.appendChild(scriptElement); 7 | 8 | window.addEventListener('SendBitcoin', function (event) { 9 | // eslint-disable-next-line no-undef 10 | chrome.runtime.sendMessage( 11 | { 12 | message: {action:'SendBitcoin',params:event.detail}, 13 | }, 14 | function () {}) 15 | }) 16 | 17 | window.addEventListener('SendTokens', function (event) { 18 | // eslint-disable-next-line no-undef 19 | chrome.runtime.sendMessage( 20 | { 21 | message: {action:'SendTokens',params:event.detail}, 22 | }, 23 | function () {}) 24 | }) 25 | 26 | window.addEventListener('SignPsbt', function (event) { 27 | // eslint-disable-next-line no-undef 28 | chrome.runtime.sendMessage( 29 | { 30 | message: {action:'SignPsbt',params:event.detail}, 31 | }, 32 | function () {}) 33 | }) 34 | 35 | window.addEventListener('SignMessage', function (event) { 36 | // eslint-disable-next-line no-undef 37 | chrome.runtime.sendMessage( 38 | { 39 | message: {action:'SignMessage',params:event.detail}, 40 | }, 41 | function () {}) 42 | }) 43 | 44 | window.addEventListener('ConnectWallet', function (event) { 45 | // eslint-disable-next-line no-undef 46 | chrome.runtime.sendMessage( 47 | { 48 | message: {action:'ConnectWallet',params:event.detail}, 49 | }, 50 | function () {}) 51 | }) 52 | 53 | window.addEventListener('VerifySign', function (event) { 54 | // eslint-disable-next-line no-undef 55 | chrome.runtime.sendMessage( 56 | { 57 | message: {action:'VerifySign',params:event.detail}, 58 | }, 59 | function () {}) 60 | }) 61 | 62 | // eslint-disable-next-line no-undef 63 | chrome.runtime.onMessage.addListener( 64 | function(request) { 65 | // Handle the message from background.js 66 | if(request.message.includes("ReturnConnectWalletInfo")){ 67 | const customEvent = new CustomEvent("ReturnConnectWalletInfo", { 68 | detail: { message: request.message } 69 | }); 70 | window.dispatchEvent(customEvent); 71 | } 72 | } 73 | ); 74 | 75 | // eslint-disable-next-line no-undef 76 | chrome.runtime.onMessage.addListener( 77 | function(request) { 78 | // Handle the message from background.js 79 | if(request.message.includes("ClientRejectConnectWalletInfo")){ 80 | const customEvent = new CustomEvent("ClientRejectConnectWalletInfo", { 81 | detail: { message: request.message } 82 | }); 83 | window.dispatchEvent(customEvent); 84 | } 85 | } 86 | ); 87 | 88 | // eslint-disable-next-line no-undef 89 | chrome.runtime.onMessage.addListener( 90 | function(request) { 91 | if(request.message.includes("ReturnSendBitcoin")){ 92 | const customEvent = new CustomEvent("ReturnSendBitcoin", { 93 | detail: { message: request.message } 94 | }); 95 | window.dispatchEvent(customEvent); 96 | } 97 | } 98 | ); 99 | 100 | // eslint-disable-next-line no-undef 101 | chrome.runtime.onMessage.addListener( 102 | function(request) { 103 | if(request.message.includes("ReturnSendTokens")){ 104 | const customEvent = new CustomEvent("ReturnSendTokens", { 105 | detail: { message: request.message } 106 | }); 107 | window.dispatchEvent(customEvent); 108 | } 109 | } 110 | ); 111 | 112 | // eslint-disable-next-line no-undef 113 | chrome.runtime.onMessage.addListener( 114 | function(request) { 115 | if(request.message.includes("ReturnSignPsbt")){ 116 | const customEvent = new CustomEvent("ReturnSignPsbt", { 117 | detail: { message: request.message } 118 | }); 119 | window.dispatchEvent(customEvent); 120 | } 121 | } 122 | ); 123 | 124 | // eslint-disable-next-line no-undef 125 | chrome.runtime.onMessage.addListener( 126 | function(request) { 127 | if(request.message.includes("ReturnErrorOnSignPsbt")){ 128 | const customEvent = new CustomEvent("ReturnErrorOnSignPsbt", { 129 | detail: { message: request.message } 130 | }); 131 | window.dispatchEvent(customEvent); 132 | } 133 | } 134 | ); 135 | 136 | // eslint-disable-next-line no-undef 137 | chrome.runtime.onMessage.addListener( 138 | function(request) { 139 | if(request.message.includes("ClientRejectSignPsbt")){ 140 | const customEvent = new CustomEvent("ClientRejectSignPsbt", { 141 | detail: { message: request.message } 142 | }); 143 | window.dispatchEvent(customEvent); 144 | } 145 | } 146 | ); 147 | 148 | // eslint-disable-next-line no-undef 149 | chrome.runtime.onMessage.addListener( 150 | function(request) { 151 | if(request.message.includes("ReturnSignMessage")){ 152 | const customEvent = new CustomEvent("ReturnSignMessage", { 153 | detail: { message: request.message } 154 | }); 155 | window.dispatchEvent(customEvent); 156 | } 157 | } 158 | ); 159 | 160 | // eslint-disable-next-line no-undef 161 | chrome.runtime.onMessage.addListener( 162 | function(request) { 163 | if(request.message.includes("ReturnErrorOnSignMessage")){ 164 | const customEvent = new CustomEvent("ReturnErrorOnSignMessage", { 165 | detail: { message: request.message } 166 | }); 167 | window.dispatchEvent(customEvent); 168 | } 169 | } 170 | ); 171 | 172 | // eslint-disable-next-line no-undef 173 | chrome.runtime.onMessage.addListener( 174 | function(request) { 175 | if(request.message.includes("ClientRejectSignMessage")){ 176 | const customEvent = new CustomEvent("ClientRejectSignMessage", { 177 | detail: { message: request.message } 178 | }); 179 | window.dispatchEvent(customEvent); 180 | } 181 | } 182 | ); 183 | 184 | // eslint-disable-next-line no-undef 185 | chrome.runtime.onMessage.addListener( 186 | function(request) { 187 | if(request.message.includes("ReturnVerifySign")){ 188 | const customEvent = new CustomEvent("ReturnVerifySign", { 189 | detail: { message: request.message } 190 | }); 191 | window.dispatchEvent(customEvent); 192 | } 193 | } 194 | ); 195 | 196 | // eslint-disable-next-line no-undef 197 | chrome.runtime.onMessage.addListener( 198 | function(request) { 199 | if(request.message.includes("ReturnErrorOnVerifySign")){ 200 | const customEvent = new CustomEvent("ReturnErrorOnVerifySign", { 201 | detail: { message: request.message } 202 | }); 203 | window.dispatchEvent(customEvent); 204 | } 205 | } 206 | ); -------------------------------------------------------------------------------- /public/inject.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | window.inspip = window.inspip || {}; 3 | 4 | window.inspip.connect = function () { 5 | const event = new CustomEvent("ConnectWallet"); 6 | window.dispatchEvent(event); 7 | return new Promise((resolve,reject) => { 8 | const eventListener = (event) => { 9 | if (event.type === "ReturnConnectWalletInfo") { 10 | window.removeEventListener("ReturnConnectWalletInfo", eventListener); 11 | const address = event.detail.message.split(';')[1]; 12 | const pubkey = event.detail.message.split(';')[2]; 13 | resolve({address,pubkey}); 14 | } 15 | if (event.type === "ClientRejectConnectWalletInfo") { 16 | window.removeEventListener("ClientRejectConnectWalletInfo", eventListener); 17 | reject(new Error("ConnectWallet rejected by client")); 18 | } 19 | }; 20 | 21 | window.addEventListener("ReturnConnectWalletInfo", eventListener); 22 | window.addEventListener("ClientRejectConnectWalletInfo", eventListener); 23 | }); 24 | }; 25 | 26 | window.inspip.sendBitcoin = function (toAddress, satoshi, feerate) { 27 | const message = {toAddress,satoshi:satoshi.toString(),feerate:feerate.toString()}; 28 | const event = new CustomEvent("SendBitcoin", {detail: message}); 29 | window.dispatchEvent(event); 30 | 31 | return new Promise((resolve) => { 32 | const eventListener = (event) => { 33 | if (event.type === "ReturnSendBitcoin") { 34 | window.removeEventListener("ReturnSendBitcoin", eventListener); 35 | const txId = event.detail.message.split(";")[1]; 36 | resolve(txId); 37 | } 38 | }; 39 | 40 | window.addEventListener("ReturnSendBitcoin", eventListener); 41 | }); 42 | }; 43 | 44 | window.inspip.sendTokens = function (ticker, id, toAddress, amount, feerate) { 45 | const message = {ticker, id, toAddress, amount:amount.toString(), feerate:feerate.toString()}; 46 | const event = new CustomEvent("SendTokens", {detail: message}); 47 | window.dispatchEvent(event); 48 | 49 | return new Promise((resolve) => { 50 | const eventListener = (event) => { 51 | if (event.type === "ReturnSendTokens") { 52 | window.removeEventListener("ReturnSendTokens", eventListener); 53 | const txId = event.detail.message.split(";")[1]; 54 | resolve(txId); 55 | } 56 | }; 57 | 58 | window.addEventListener("ReturnSendTokens", eventListener); 59 | }); 60 | }; 61 | 62 | window.inspip.signPsbt = function (psbt, toSignInputs, autoFinalized) { 63 | const message = {psbt, toSignInputs:JSON.stringify(toSignInputs), autoFinalized:autoFinalized.toString()}; 64 | const event = new CustomEvent("SignPsbt", {detail: message}); 65 | window.dispatchEvent(event); 66 | 67 | return new Promise((resolve,reject) => { 68 | const eventListener = (event) => { 69 | if (event.type === "ReturnSignPsbt") { 70 | window.removeEventListener("ReturnSignPsbt", eventListener); 71 | const signed = event.detail.message.split(';')[1]; 72 | resolve(signed); 73 | } 74 | if (event.type === "ReturnErrorOnSignPsbt") { 75 | window.removeEventListener("ReturnErrorOnSignPsbt", eventListener); 76 | const error = event.detail.message.split(';')[1]; 77 | reject(new Error(error)); 78 | } 79 | if (event.type === "ClientRejectSignPsbt") { 80 | window.removeEventListener("ClientRejectSignPsbt", eventListener); 81 | reject(new Error("SignPsbt rejected by client")); 82 | } 83 | }; 84 | 85 | window.addEventListener("ReturnSignPsbt", eventListener); 86 | window.addEventListener("ReturnErrorOnSignPsbt", eventListener); 87 | window.addEventListener("ClientRejectSignPsbt", eventListener); 88 | }); 89 | }; 90 | 91 | window.inspip.signMessage = function (msg) { 92 | const message = {msg}; 93 | const event = new CustomEvent("SignMessage", {detail: message}); 94 | window.dispatchEvent(event); 95 | 96 | return new Promise((resolve,reject) => { 97 | const eventListener = (event) => { 98 | if (event.type === "ReturnSignMessage") { 99 | window.removeEventListener("ReturnSignMessage", eventListener); 100 | const signature = event.detail.message.split(';')[1]; 101 | resolve(signature); 102 | } 103 | if (event.type === "ReturnErrorOnSignMessage") { 104 | window.removeEventListener("ReturnErrorOnSignMessage", eventListener); 105 | reject(new Error("Error on SignMessage event occurred.")); 106 | } 107 | if (event.type === "ClientRejectSignMessage") { 108 | window.removeEventListener("ClientRejectSignMessage", eventListener); 109 | reject(new Error("SignMessage rejected by client")); 110 | } 111 | }; 112 | 113 | window.addEventListener("ReturnSignMessage", eventListener); 114 | window.addEventListener("ReturnErrorOnSignMessage", eventListener); 115 | window.addEventListener("ClientRejectSignMessage", eventListener); 116 | }); 117 | }; 118 | 119 | window.inspip.verifySign = function (msg, signature) { 120 | const message = {msg, signature}; 121 | const event = new CustomEvent("VerifySign", {detail: message}); 122 | window.dispatchEvent(event); 123 | 124 | return new Promise((resolve,reject) => { 125 | const eventListener = (event) => { 126 | if (event.type === "ReturnVerifySign") { 127 | window.removeEventListener("ReturnVerifySign", eventListener); 128 | const result = event.detail.message.split(';')[1] === "true" ? true : false; 129 | resolve(result); 130 | } 131 | if (event.type === "ReturnErrorOnVerifySign") { 132 | window.removeEventListener("ReturnErrorOnVerifySign", eventListener); 133 | reject(new Error("Error on VerifySign event occurred.")); 134 | } 135 | }; 136 | 137 | window.addEventListener("ReturnVerifySign", eventListener); 138 | window.addEventListener("ReturnErrorOnVerifySign", eventListener); 139 | }); 140 | }; 141 | })(); 142 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inscrib3/inspip/3671b68eb56adc743fe3f4498b3306932b5340da/public/logo.png -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "Inspip", 4 | "permissions": [ 5 | "activeTab" 6 | ], 7 | "content_scripts": [ 8 | { 9 | "run_at": "document_end", 10 | "matches": ["http://*/*", "https://*/*"], 11 | "js": ["content.js"] 12 | } 13 | ], 14 | "web_accessible_resources": [ 15 | { 16 | "resources": ["inject.js"], 17 | "matches": [""] 18 | } 19 | ], 20 | "background": { 21 | "service_worker": "background.js", 22 | "type": "module" 23 | }, 24 | "description": "Inspip | Pipe Wallet Extension", 25 | "version": "0.1.12", 26 | "action": { 27 | "default_popup": "index.html", 28 | "default_icon": "logo.png" 29 | } 30 | } -------------------------------------------------------------------------------- /src/app/app-context.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useCallback, useEffect, useRef, useState } from 'react'; 2 | import { editWallet, updateStoredWallet } from '../bitcoin/wallet-storage'; 3 | 4 | export type Utxo = { 5 | protocol?: "pipe" | "ordinals"; 6 | txid: string, 7 | hex?: string, 8 | status: { 9 | confirmed: boolean, 10 | }, 11 | vout: number, 12 | value: number, 13 | tick?: string, 14 | id?: number, 15 | dec?: number, 16 | amt?: string, 17 | }; 18 | 19 | export const AppContext = createContext<{ 20 | network: string, 21 | setNetwork: (network: string) => void, 22 | account: any, 23 | setAccount: (account: any) => void, 24 | currentAddress: string, 25 | setCurrentAddress: (address: string, index: number) => void, 26 | addresses: number[], 27 | setAddresses: (addresses: number[]) => void, 28 | loading: boolean, 29 | feerate: number, 30 | setFeerate: (feerate: number) => void, 31 | tokens: { tick: string, id: number, dec: number }[], 32 | setTokens: (tokens: { tick: string, id: number, dec: number }[]) => void, 33 | fetchUtxos: () => Promise, 34 | signPsbt: any, 35 | setSignPsbt: any, 36 | signMessage: any, 37 | setSignMessage: any, 38 | verifySign: any, 39 | setVerifySign: any 40 | }>({ 41 | account: {}, 42 | loading: false, 43 | setAccount: () => undefined, 44 | network: 'mainnet', 45 | setNetwork: () => undefined, 46 | currentAddress: '', 47 | setCurrentAddress: () => undefined, 48 | addresses: [], 49 | setAddresses: () => undefined, 50 | feerate: 0, 51 | setFeerate: () => undefined, 52 | tokens: [], 53 | setTokens: () => undefined, 54 | fetchUtxos: async () => [], 55 | signPsbt: {}, 56 | setSignPsbt: () => undefined, 57 | signMessage: {}, 58 | setSignMessage: () => undefined, 59 | verifySign: {}, 60 | setVerifySign: () => undefined, 61 | }); 62 | 63 | export type Tx = { 64 | txid: string; 65 | vin: { 66 | txid: string; 67 | vout: number; 68 | }[]; 69 | vout: { 70 | index: number; 71 | scriptpubkey_address: string; 72 | value: number; 73 | }[]; 74 | status?: { 75 | confirmed: boolean; 76 | } 77 | }; 78 | 79 | export type TxsStorage = { 80 | last_txid: string; 81 | data: { 82 | [txid: string]: Tx; 83 | }; 84 | }; 85 | 86 | export interface IndexerToken { 87 | beneficiaryAddress: string; 88 | block: number; 89 | bvo: number; 90 | collectionAddress: string; 91 | collectionNumber: number; 92 | createdAt: string; 93 | decimals: number; 94 | id: number; 95 | limit: number; 96 | maxSupply: number; 97 | pid: number; 98 | amount?: number; 99 | remaining: number; 100 | ticker: string; 101 | txId: string; 102 | updatedAt: string; 103 | vo: number; 104 | vout: number; 105 | metadata?: string; 106 | mime?: string; 107 | ref?: string; 108 | traits?: string[]; 109 | } 110 | 111 | export interface AppProviderProps { 112 | children: React.ReactNode; 113 | } 114 | 115 | // Import the functions you need from the SDKs you need 116 | import { initializeApp, FirebaseApp } from "firebase/app"; 117 | import { getAnalytics, Analytics } from "firebase/analytics"; 118 | import { selectAllOrdinalsUnspents } from '../transfer/select-all-ordinals-unspents'; 119 | import { selectAllPipeUnspents } from '../transfer/select-all-pipe-unspents'; 120 | // TODO: Add SDKs for Firebase products that you want to use 121 | // https://firebase.google.com/docs/web/setup#available-libraries 122 | 123 | // Your web app's Firebase configuration 124 | // For Firebase JS SDK v7.20.0 and later, measurementId is optional 125 | const firebaseConfig = { 126 | apiKey: "AIzaSyBC77tDyyuddrsc_Jdm0BcsKNoFl5BXWOQ", 127 | authDomain: "inspip-wallet.firebaseapp.com", 128 | projectId: "inspip-wallet", 129 | storageBucket: "inspip-wallet.appspot.com", 130 | messagingSenderId: "734952055419", 131 | appId: "1:734952055419:web:a5cdb017c0d7423c99f8de", 132 | measurementId: "G-523R85SP1N" 133 | }; 134 | 135 | export const AppProvider = (props: AppProviderProps) => { 136 | const [account, setAccount] = useState({}); 137 | const [network, _setNetwork] = useState('mainnet'); 138 | const [addresses, _setAddresses] = useState([]); 139 | const [currentAddress, _setCurrentAddress] = useState(''); 140 | const [feerate, setFeerate] = useState(0); 141 | const [tokens, setTokens] = useState<{ tick: string, id: number, dec: number }[]>([]); 142 | 143 | const [signPsbt, setSignPsbt] = useState({}); 144 | const [signMessage, setSignMessage] = useState({}); 145 | const [verifySign, setVerifySign] = useState({}); 146 | const loading = useRef(false); 147 | const [firebase, setFirebase] = useState(null); 148 | const [, setAnalytics] = useState(null); 149 | 150 | useEffect(() => { 151 | if (firebase) return; 152 | const nextFirebase = initializeApp(firebaseConfig); 153 | setAnalytics(getAnalytics(nextFirebase)); 154 | setFirebase(nextFirebase); 155 | }, [firebase]); 156 | 157 | interface TickerData { 158 | tick: string; 159 | id: number; 160 | dec: number; 161 | } 162 | 163 | const getUniqueTickers = (data: any) : TickerData[] =>{ 164 | const uniqueTickers = data.reduce((acc:any, curr:any) => { 165 | const exists = acc.find((item:{ 166 | tick: string, 167 | id: number, 168 | dec: number, 169 | }) => item.tick === curr.tick && item.id === curr.id && item.dec === curr.dec); 170 | if (!exists) { 171 | acc.push(curr); 172 | } 173 | return acc; 174 | }, []); 175 | return uniqueTickers; 176 | } 177 | 178 | const fetchUtxos = useCallback(async (): Promise => { 179 | if (loading.current || currentAddress === '') return []; 180 | 181 | loading.current = true; 182 | 183 | const pipeUnspents = await selectAllPipeUnspents({ 184 | network: network as "mainnet" | "testnet", 185 | address: currentAddress, 186 | }); 187 | 188 | const pipeUnspentFormatted = pipeUnspents.map((pipeUnspent) => { 189 | return { 190 | protocol: "pipe", 191 | tick: pipeUnspent.ticker, 192 | id: pipeUnspent.id, 193 | dec: pipeUnspent.decimals, 194 | amt: pipeUnspent.amount, 195 | status: { 196 | confirmed: true, 197 | }, 198 | }; 199 | }); 200 | 201 | const uniqueTickers: TickerData[] = getUniqueTickers(pipeUnspentFormatted); 202 | setTokens(uniqueTickers); 203 | 204 | const ordinalsUnspents = await selectAllOrdinalsUnspents({ 205 | network: network as "mainnet" | "testnet", 206 | address: currentAddress, 207 | }); 208 | 209 | const ordinalsUnspentFormatted = ordinalsUnspents.map(() => { 210 | return { 211 | protocol: "ordinals", 212 | status: { 213 | confirmed: true, 214 | }, 215 | }; 216 | }); 217 | 218 | loading.current = false; 219 | return [...pipeUnspentFormatted,...ordinalsUnspentFormatted]; 220 | }, [currentAddress, network]); 221 | 222 | const setAddresses = (addresses: number[]) => { 223 | _setAddresses(addresses); 224 | editWallet('', addresses); 225 | }; 226 | 227 | const setCurrentAddress = (address: string, index: number) => { 228 | _setCurrentAddress(address); 229 | editWallet(address, [], index); 230 | }; 231 | 232 | const setNetwork = (network: string) => { 233 | _setNetwork(network); 234 | updateStoredWallet('network', network); 235 | }; 236 | 237 | return ( 238 | 262 | {props.children} 263 | 264 | ); 265 | } 266 | -------------------------------------------------------------------------------- /src/app/app.hook.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { AppContext } from "./app-context"; 3 | 4 | export const useApp = () => useContext(AppContext); -------------------------------------------------------------------------------- /src/app/app.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Button, Image, Text } from "grommet"; 2 | import { useNavigate } from "react-router-dom"; 3 | import { RoutePath } from "../router"; 4 | import { useEffect } from "react"; 5 | import { Layout } from "../components"; 6 | import { useSearchParams } from "react-router-dom"; 7 | import { useApp } from "."; 8 | 9 | export const App = () => { 10 | const navigate = useNavigate(); 11 | const app = useApp(); 12 | const [searchParams] = useSearchParams(); 13 | const params = { 14 | ticker: searchParams.get("ticker"), 15 | id: searchParams.get("id"), 16 | toAddress: searchParams.get("toAddress"), 17 | satoshi: searchParams.get("satoshi") || searchParams.get("amount"), 18 | feerate: searchParams.get("feerate"), 19 | }; 20 | 21 | useEffect(()=>{ 22 | if (searchParams.get("action") === "SignPsbt") { 23 | app.setSignPsbt({ 24 | psbt:(searchParams.get("psbt") || "").replace(/\s/g,'+'), 25 | toSignInputs:(JSON.parse(searchParams.get("toSignInputs") || "")), 26 | autoFinalized:searchParams.get("autoFinalized") === 'true' ? true : false, 27 | }) 28 | } 29 | if (searchParams.get("action") === "SignMessage") { 30 | app.setSignMessage({ 31 | msg:searchParams.get("msg"), 32 | type:searchParams.get("type"), 33 | }) 34 | } 35 | if (searchParams.get("action") === "VerifySign") { 36 | app.setVerifySign({ 37 | msg:searchParams.get("msg"), 38 | signature:(searchParams.get("signature") || "").replace(/\s/g,'+'), 39 | }) 40 | } 41 | },[app, searchParams]) 42 | 43 | const createWallet = () => navigate(RoutePath.CreateWallet); 44 | const restoreWallet = () => navigate(RoutePath.RestoreWallet); 45 | const connectWallet = () => navigate(RoutePath.ConnectWallet); 46 | const send = (data: any) => navigate(RoutePath.Send, { state: data }); 47 | 48 | useEffect(() => { 49 | if (searchParams.get("toAddress")) { 50 | setTimeout(() => { 51 | send(params); 52 | }, 500); 53 | } 54 | if ( 55 | searchParams.get("action") && 56 | searchParams.get("action") === "ConnectWallet" 57 | ) { 58 | setTimeout(() => { 59 | connectWallet(); 60 | }, 500); 61 | } 62 | // eslint-disable-next-line react-hooks/exhaustive-deps 63 | }, [searchParams]); 64 | 65 | useEffect(() => { 66 | if (localStorage.getItem("wallet")) { 67 | navigate(RoutePath.Password); 68 | } 69 | }, [navigate]); 70 | 71 | return ( 72 | 73 | 74 | 75 | 76 | 77 | 78 | Inspip | Pipe Wallet 79 | 80 | 81 | 82 | Create & Store your Pipe DMT and ART in the world's first Open 83 | Source Chrome wallet for Pipe! 84 | 85 | 86 |