diff --git a/jest.config.js b/jest.config.js index d94ed54ff..0e9139901 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,5 +1,7 @@ /** @type {import('ts-jest').JestConfigWithTsJest} */ require('dotenv').config(); + + process.env.NFT_CONTRACT_ID = 'foo.near'; // FIXME: dotenv seems to load for the jest debugger, but not when running npm test module.exports = { @@ -11,5 +13,6 @@ module.exports = { moduleNameMapper: { '@near-wallet-selector/meteor-wallet': '/jest.stub.js', '@here-wallet/core/build/strategy': '/jest.stub.js', + '\\.css$': '/jest.stub.js', }, }; diff --git a/package-lock.json b/package-lock.json index 4a8cd03be..a338e0909 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13790,6 +13790,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/isomorphic-textencoder/-/isomorphic-textencoder-1.0.1.tgz", "integrity": "sha512-676hESgHullDdHDsj469hr+7t3i/neBKU9J7q1T4RHaWwLAsaQnywC0D1dIUId0YZ+JtVrShzuBk1soo0+GVcQ==", + "peer": true, "dependencies": { "fast-text-encoding": "^1.0.0" } @@ -13798,6 +13799,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz", "integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==", + "peer": true, "dependencies": { "node-fetch": "^2.6.1", "unfetch": "^4.2.0" @@ -23653,7 +23655,8 @@ "node_modules/unfetch": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", - "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==" + "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==", + "peer": true }, "node_modules/unique-filename": { "version": "3.0.0", @@ -25241,13 +25244,15 @@ "@near-wallet-selector/sender": "^8.0.3", "@near-wallet-selector/wallet-connect": "^8.0.3", "bs58": "^5.0.0", - "isomorphic-textencoder": "^1.0.1", "js-sha256": "^0.9.0", "rxjs": "^7.5.7" }, "devDependencies": { "jest-environment-jsdom": "^29.1.1", "zod": "^3.9.0" + }, + "peerDependencies": { + "isomorphic-textencoder": "^1.0.1" } }, "packages/auth/node_modules/@mintbase-js/sdk": { @@ -25314,14 +25319,16 @@ "version": "0.5.0-beta-prerelease.1", "license": "MIT", "dependencies": { - "graphql-request": "^5.0.0", - "isomorphic-fetch": "^3.0.0" + "graphql-request": "^5.0.0" }, "devDependencies": { "@graphql-codegen/cli": "^2.13.7", "@graphql-codegen/client-preset": "1.1.1", "@graphql-codegen/introspection": "2.2.1", "@mintbase-js/sdk": "^0.5.0-beta-prerelease.1" + }, + "peerDependencies": { + "isomorphic-fetch": "^3.0.0" } }, "packages/data/node_modules/extract-files": { @@ -25367,12 +25374,13 @@ "version": "0.5.0-beta-prerelease.1", "license": "MIT", "dependencies": { - "@mintbase-js/auth": "0.5.0-beta.1", - "@mintbase-js/data": "^0.5.0-beta-prerelease.1", - "@mintbase-js/sdk": "0.4.1-fix-sign-transaction-0872c2d.0", + "@mintbase-js/wallet": "0.5.0-fixes-wallet-selector-101ffee.0", "@near-wallet-selector/core": "^8.0.3", + "@near-wallet-selector/here-wallet": "^8.0.3", + "@near-wallet-selector/meteor-wallet": "^8.0.3", "@near-wallet-selector/modal-ui": "^8.0.3", - "near-api-js": "^2.1.3", + "@near-wallet-selector/my-near-wallet": "*", + "@near-wallet-selector/near-wallet": "^8.0.3", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -25381,51 +25389,10 @@ "@testing-library/user-event": "^14.4.3" } }, - "packages/react/node_modules/@mintbase-js/auth": { - "version": "0.5.0-beta.1", - "resolved": "https://registry.npmjs.org/@mintbase-js/auth/-/auth-0.5.0-beta.1.tgz", - "integrity": "sha512-Z2pqT8XyKrPS6/PSRTOgF//HMPPJiPmiIBybJ3k/l9hz80RqkP6ZsuE7m+barMr2tkCYOCmFg6Y8J7Uy4lht2g==", - "dependencies": { - "@mintbase-js/sdk": "0.5.0-beta.0", - "@mintbase-js/wallet": "0.5.0-beta.0", - "@near-wallet-selector/core": "^8.0.3", - "@near-wallet-selector/default-wallets": "^8.0.3", - "@near-wallet-selector/here-wallet": "^8.0.3", - "@near-wallet-selector/ledger": "^8.0.3", - "@near-wallet-selector/meteor-wallet": "^8.0.3", - "@near-wallet-selector/modal-ui": "^8.0.3", - "@near-wallet-selector/my-near-wallet": "*", - "@near-wallet-selector/near-wallet": "^8.0.3", - "@near-wallet-selector/sender": "^8.0.3", - "@near-wallet-selector/wallet-connect": "^8.0.3", - "bs58": "^5.0.0", - "isomorphic-textencoder": "^1.0.1", - "js-sha256": "^0.9.0", - "rxjs": "^7.5.7" - } - }, - "packages/react/node_modules/@mintbase-js/auth/node_modules/@mintbase-js/sdk": { - "version": "0.5.0-beta.0", - "resolved": "https://registry.npmjs.org/@mintbase-js/sdk/-/sdk-0.5.0-beta.0.tgz", - "integrity": "sha512-cT/8oOoFw1R3gk8A6TAfLhabT4pr0fwSu0lsUU1YYaKI0EWjhghkMMxCRtaWU8EDneVLxAK+SNa2B1zD0cXoTA==", - "dependencies": { - "bn.js": "5.2.1", - "near-api-js": "^2.1.3" - } - }, - "packages/react/node_modules/@mintbase-js/sdk": { - "version": "0.4.1-fix-sign-transaction-0872c2d.0", - "resolved": "https://registry.npmjs.org/@mintbase-js/sdk/-/sdk-0.4.1-fix-sign-transaction-0872c2d.0.tgz", - "integrity": "sha512-SRIe75o3Ymc3zQ3Q5Ghu+ibilP0xgvDy9Pn1rFgni8B5V07pwNJSLBSDuPgkd4UbRy0jk+OlyvCCyfAQl1qsHA==", - "dependencies": { - "bn.js": "5.2.1", - "near-api-js": "^2.1.3" - } - }, "packages/react/node_modules/@mintbase-js/wallet": { - "version": "0.5.0-beta.0", - "resolved": "https://registry.npmjs.org/@mintbase-js/wallet/-/wallet-0.5.0-beta.0.tgz", - "integrity": "sha512-mhpUYX8yCFcSzkj5VAJn01pbXgXxK67Na8Y0x88EzuRRqnleHb60LZFXEw7wuwynFjaZseKeQY/yd2Pti7GgqQ==", + "version": "0.5.0-fixes-wallet-selector-101ffee.0", + "resolved": "https://registry.npmjs.org/@mintbase-js/wallet/-/wallet-0.5.0-fixes-wallet-selector-101ffee.0.tgz", + "integrity": "sha512-pHkZ9iTZ7dW4nocmshUHx3cyzXHLjvZzW8ZXNW78ixtuksjJxWB4tQPIhHxycAPXFPKrwXYuuN35U8BIC8ZKBQ==", "dependencies": { "@near-wallet-selector/core": "^8.5.4", "bn.js": "^5.2.1", @@ -25475,12 +25442,14 @@ "license": "MIT", "dependencies": { "@types/node": "^16.0.0", - "bn.js": "^5.2.1", - "isomorphic-unfetch": "^3.1.0" + "bn.js": "^5.2.1" }, "devDependencies": { "@mintbase-js/sdk": "^0.5.0-beta-prerelease.1", "@types/bn.js": "^5.1.1" + }, + "peerDependencies": { + "isomorphic-unfetch": "^3.1.0" } }, "packages/rpc/node_modules/@types/node": { @@ -29165,7 +29134,6 @@ "@near-wallet-selector/sender": "^8.0.3", "@near-wallet-selector/wallet-connect": "^8.0.3", "bs58": "^5.0.0", - "isomorphic-textencoder": "^1.0.1", "jest-environment-jsdom": "^29.1.1", "js-sha256": "^0.9.0", "rxjs": "^7.5.7", @@ -29233,8 +29201,7 @@ "@graphql-codegen/client-preset": "1.1.1", "@graphql-codegen/introspection": "2.2.1", "@mintbase-js/sdk": "^0.5.0-beta-prerelease.1", - "graphql-request": "^5.0.0", - "isomorphic-fetch": "^3.0.0" + "graphql-request": "^5.0.0" }, "dependencies": { "extract-files": { @@ -29268,65 +29235,23 @@ "@mintbase-js/react": { "version": "file:packages/react", "requires": { - "@mintbase-js/auth": "0.5.0-beta.1", - "@mintbase-js/data": "^0.5.0-beta-prerelease.1", - "@mintbase-js/sdk": "0.4.1-fix-sign-transaction-0872c2d.0", + "@mintbase-js/wallet": "0.5.0-fixes-wallet-selector-101ffee.0", "@near-wallet-selector/core": "^8.0.3", + "@near-wallet-selector/here-wallet": "^8.0.3", + "@near-wallet-selector/meteor-wallet": "^8.0.3", "@near-wallet-selector/modal-ui": "^8.0.3", + "@near-wallet-selector/my-near-wallet": "*", + "@near-wallet-selector/near-wallet": "^8.0.3", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^14.4.3", - "near-api-js": "^2.1.3", "react": "^18.2.0", "react-dom": "^18.2.0" }, "dependencies": { - "@mintbase-js/auth": { - "version": "0.5.0-beta.1", - "resolved": "https://registry.npmjs.org/@mintbase-js/auth/-/auth-0.5.0-beta.1.tgz", - "integrity": "sha512-Z2pqT8XyKrPS6/PSRTOgF//HMPPJiPmiIBybJ3k/l9hz80RqkP6ZsuE7m+barMr2tkCYOCmFg6Y8J7Uy4lht2g==", - "requires": { - "@mintbase-js/sdk": "0.5.0-beta.0", - "@mintbase-js/wallet": "0.5.0-beta.0", - "@near-wallet-selector/core": "^8.0.3", - "@near-wallet-selector/default-wallets": "^8.0.3", - "@near-wallet-selector/here-wallet": "^8.0.3", - "@near-wallet-selector/ledger": "^8.0.3", - "@near-wallet-selector/meteor-wallet": "^8.0.3", - "@near-wallet-selector/modal-ui": "^8.0.3", - "@near-wallet-selector/my-near-wallet": "*", - "@near-wallet-selector/near-wallet": "^8.0.3", - "@near-wallet-selector/sender": "^8.0.3", - "@near-wallet-selector/wallet-connect": "^8.0.3", - "bs58": "^5.0.0", - "isomorphic-textencoder": "^1.0.1", - "js-sha256": "^0.9.0", - "rxjs": "^7.5.7" - }, - "dependencies": { - "@mintbase-js/sdk": { - "version": "0.5.0-beta.0", - "resolved": "https://registry.npmjs.org/@mintbase-js/sdk/-/sdk-0.5.0-beta.0.tgz", - "integrity": "sha512-cT/8oOoFw1R3gk8A6TAfLhabT4pr0fwSu0lsUU1YYaKI0EWjhghkMMxCRtaWU8EDneVLxAK+SNa2B1zD0cXoTA==", - "requires": { - "bn.js": "5.2.1", - "near-api-js": "^2.1.3" - } - } - } - }, - "@mintbase-js/sdk": { - "version": "0.4.1-fix-sign-transaction-0872c2d.0", - "resolved": "https://registry.npmjs.org/@mintbase-js/sdk/-/sdk-0.4.1-fix-sign-transaction-0872c2d.0.tgz", - "integrity": "sha512-SRIe75o3Ymc3zQ3Q5Ghu+ibilP0xgvDy9Pn1rFgni8B5V07pwNJSLBSDuPgkd4UbRy0jk+OlyvCCyfAQl1qsHA==", - "requires": { - "bn.js": "5.2.1", - "near-api-js": "^2.1.3" - } - }, "@mintbase-js/wallet": { - "version": "0.5.0-beta.0", - "resolved": "https://registry.npmjs.org/@mintbase-js/wallet/-/wallet-0.5.0-beta.0.tgz", - "integrity": "sha512-mhpUYX8yCFcSzkj5VAJn01pbXgXxK67Na8Y0x88EzuRRqnleHb60LZFXEw7wuwynFjaZseKeQY/yd2Pti7GgqQ==", + "version": "0.5.0-fixes-wallet-selector-101ffee.0", + "resolved": "https://registry.npmjs.org/@mintbase-js/wallet/-/wallet-0.5.0-fixes-wallet-selector-101ffee.0.tgz", + "integrity": "sha512-pHkZ9iTZ7dW4nocmshUHx3cyzXHLjvZzW8ZXNW78ixtuksjJxWB4tQPIhHxycAPXFPKrwXYuuN35U8BIC8ZKBQ==", "requires": { "@near-wallet-selector/core": "^8.5.4", "bn.js": "^5.2.1", @@ -29374,8 +29299,7 @@ "@mintbase-js/sdk": "^0.5.0-beta-prerelease.1", "@types/bn.js": "^5.1.1", "@types/node": "^16.0.0", - "bn.js": "^5.2.1", - "isomorphic-unfetch": "^3.1.0" + "bn.js": "^5.2.1" }, "dependencies": { "@types/node": { @@ -36609,6 +36533,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/isomorphic-textencoder/-/isomorphic-textencoder-1.0.1.tgz", "integrity": "sha512-676hESgHullDdHDsj469hr+7t3i/neBKU9J7q1T4RHaWwLAsaQnywC0D1dIUId0YZ+JtVrShzuBk1soo0+GVcQ==", + "peer": true, "requires": { "fast-text-encoding": "^1.0.0" } @@ -36617,6 +36542,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz", "integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==", + "peer": true, "requires": { "node-fetch": "^2.6.1", "unfetch": "^4.2.0" @@ -44507,7 +44433,8 @@ "unfetch": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", - "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==" + "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==", + "peer": true }, "unique-filename": { "version": "3.0.0", diff --git a/packages/auth/package.json b/packages/auth/package.json index 5c2eb7606..302ffa95a 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -32,12 +32,14 @@ "@near-wallet-selector/sender": "^8.0.3", "@near-wallet-selector/wallet-connect": "^8.0.3", "bs58": "^5.0.0", - "isomorphic-textencoder": "^1.0.1", "js-sha256": "^0.9.0", "rxjs": "^7.5.7" }, "devDependencies": { "jest-environment-jsdom": "^29.1.1", "zod": "^3.9.0" + }, + "peerDependencies": { + "isomorphic-textencoder": "^1.0.1" } } diff --git a/packages/data/package.json b/packages/data/package.json index ff0307caf..f3205fb97 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -21,7 +21,9 @@ "author": "", "license": "MIT", "dependencies": { - "graphql-request": "^5.0.0", + "graphql-request": "^5.0.0" + }, + "peerDependencies": { "isomorphic-fetch": "^3.0.0" }, "devDependencies": { diff --git a/packages/react/README.md b/packages/react/README.md index 299162916..d886a5568 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -24,17 +24,6 @@ You can check a [quick example of Simple Login](https://github.com/Mintbase/star - [MintbaseWalletContextProvider (default)](#mintbasewalletcontextprovider) : The default Mintbase Wallet provider -- [Hooks](#Hooks): a variety of hooks to make data fetching and transactions from our SDK/Data modules super easy in your React apps. - -## Deprecated - -we still give support, but is not currently being used or maintained. - -- [Config consts (deprecated)](#config) : Config the network and global variables on mintbase-js packages - -- [WalletContextProvider (deprecated)](#walletcontextprovider) : the provider that will wrap the wallets to make it work in your application - -- [WalletContext (deprecated)](#walletcontext): helper with methods to use the power of near-wallet-selector # Installing @@ -81,224 +70,18 @@ the default way of interacting with Mintbase Wallet is using the MintbaseWalletC import { MintbaseWalletContextProvider } from '@mintbase-js/react' - + contractAddress="mycontract.mintbase1.near" + network="mainnet" + callbackUrl="https://www.mywebsite.com/callback" +> - ``` -{% endcode %} - - -# Hooks - -a variety of hooks to make data fetching and transactions from our SDK/Data modules super easy in your React apps. - -### useOwnedNftsByStore - -hook to fetch owned nfts by store (contractAddress) - -_params:_ - -`ownerId: string` - -`contractAddress: string` - -`pagination: { limit: number; offset?: number}` - -### useTokenById - -hook to fetch token by Id - -_params:_ - -`tokenId: string,contractAddress: string` - -# config vars (deprecated) - -read about config global variables on: [Config SDK method](https://docs.mintbase.io/dev/mintbase-sdk-ref/sdk/config) - -**_ALSO RECOMMENDED TO DO:_** you can set network and contractAddress (the one from your dapp/mintbase store) straight on the WalletContextProvider too like this: - -{% code title="app.tsx" overflow="wrap" lineNumbers="true" %} - -```typescript - - - - - -``` - -{% endcode %} - -# WalletContextProvider (deprecated) - -WalletContextProvider is the provider you should wrap on your regular app.tsx/app.jsx file so that your application can work with our Wallet Selector: - -Example usage in React Apps: - -- Its important to install `@near-wallet-selector/modal-ui` - -- Next.js example: - -{% code title="app.tsx" overflow="wrap" lineNumbers="true" %} - -```typescript - - - -import type { AppProps } from 'next/app'; -import { WalletContextProvider } from '@mintbase-js/react'; -import '@near-wallet-selector/modal-ui/styles.css'; - -function MyApp({ Component, pageProps }: AppProps): JSX.Element { - return ( - - - - - - ); -} -``` - -{% endcode %} - -# WalletContext - -The WalletContext provides methods for: - -1. Connecting NEAR accounts to your applications via [near-wallet-selector](https://github.com/near/wallet-selector/) - -2. Interacting with @mintbase-js/sdk - -The following props are provided to consumers of the `WalletContext.Provider`: - -{% code title="NearWalletConnector.ts" overflow="wrap" lineNumbers="true" %} - -```typescript - - - -export type WalletContext = { - -// a reference to the near wallet selector - -selector: WalletSelector; - -// the modal window that can be opened and closed - -modal: WalletSelectorModal; - -// an array of connected accounts - -accounts: AccountState[]; - -// the current active account e.g. cooluser5.near - -activeAccountId: string | null; - -// wether or not a wallet is connected, can be derived from presense of activeAccountId - -isConnected: boolean; - -// true when the wallet selector modal is opened via connect() method - -isWaitingForConnection: boolean; - -// null when no error present, contains error messages from wallet selector otherwise - -errorMessage: string | null; - -// used to open the modal and connect to a NEAR account - -connect: () => Promise; - -// disconnect entirely from NEAR account - -disconnect: () => Promise; - -// can be used to sign messages used to verify wallet ownership - -signMessage: (params: VerifyOwnerParams) => Promise; -} - -``` - -{% endcode %} - -Example usage in React components: - -{% code title="NearWalletConnector.ts" overflow="wrap" lineNumbers="true" %} - -```typescript -import { useWallet } from '@mintbase-js/react' - -const NearWalletConnector = () => { - -const { - connect, - disconnect, - activeAccountId, - selector, - isConnected, - errorMessage, -} = useWallet(); - - -const signTxn = async () => { -const wallet = await selector.wallet(); - -// ... call mintbase SDK methods with wallet as signingOption arg -} - -if (errorMessage) { -return ( -
-

Major bummer! Could not connect to NEAR {errorMessage}

- -
-) -} - -if (!isConnected) { -return -} - -return ( -
-

You are connected as {activeAccountId}

- - -
- ) -} - - - -``` - -{% endcode %} - - # Troubleshooting The wallet runs only on client-side. -it might ask you to install - -``` - "@near-wallet-selector/modal-ui": "^8.7.1", - "isomorphic-unfetch": "^3.1.0", -``` - -if you have any extra problems with ```fs``` you can copy this -[next.config.js](https://github.com/Mintbase/starter/blob/main/next-js/next.config.js) example - Any other questions or issues you can contact support on our [Telegram Channel](https://telegram.me/mintdev). diff --git a/packages/react/package.json b/packages/react/package.json index 85c6571a3..e6eb5b07d 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -23,12 +23,13 @@ "@testing-library/user-event": "^14.4.3" }, "dependencies": { - "@mintbase-js/auth": "0.5.0-beta.1", - "@mintbase-js/data": "^0.5.0-beta-prerelease.1", - "@mintbase-js/sdk": "0.4.1-fix-sign-transaction-0872c2d.0", + "@mintbase-js/wallet": "0.5.0-fixes-wallet-selector-101ffee.0", "@near-wallet-selector/core": "^8.0.3", "@near-wallet-selector/modal-ui": "^8.0.3", - "near-api-js": "^2.1.3", + "@near-wallet-selector/my-near-wallet": "*", + "@near-wallet-selector/near-wallet": "^8.0.3", + "@near-wallet-selector/meteor-wallet": "^8.0.3", + "@near-wallet-selector/here-wallet": "^8.0.3", "react": "^18.2.0", "react-dom": "^18.2.0" } diff --git a/packages/react/src/WalletContext.test.tsx b/packages/react/src/MintbaseWalletContext.test.tsx similarity index 51% rename from packages/react/src/WalletContext.test.tsx rename to packages/react/src/MintbaseWalletContext.test.tsx index db54184a0..2feaba867 100644 --- a/packages/react/src/WalletContext.test.tsx +++ b/packages/react/src/MintbaseWalletContext.test.tsx @@ -5,17 +5,16 @@ import React from 'react'; import { render, screen, act, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { useWallet, WalletContextProvider } from './WalletContext'; +import { useMbWallet, MintbaseWalletContextProvider } from './MintbaseWalletContext'; import { setupWalletSelectorComponents, registerWalletAccountsSubscriber, disconnectFromWalletSelector, connectWalletSelector, pollForWalletConnection, -} from '@mintbase-js/auth/lib/wallet'; +} from './wallet/wallet'; - -jest.mock('@mintbase-js/auth/lib/wallet'); +jest.mock('./wallet/wallet'); globalThis.mbjs = { @@ -37,6 +36,7 @@ globalThis.mbjs = { }, }; + describe('WalletContext', () => { // test('should provide error message when setup goes wrong', async () => { // // throw on startup @@ -67,89 +67,89 @@ describe('WalletContext', () => { // }); // }); - test('should provide connection error message when polling times out', async () => { - // throw on startup - const errorMessageToDisplay = 'oh snap!'; - (setupWalletSelectorComponents as jest.Mock) - .mockResolvedValue({ - modal: 'foo', - selector: 'bar', - }); - (registerWalletAccountsSubscriber as jest.Mock) - .mockImplementation((callback) => { - callback(['whatever']); - return { - unsubscribe: jest.fn(), - }; - }); - (pollForWalletConnection as jest.Mock) - .mockRejectedValueOnce(new Error(errorMessageToDisplay)); - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const ContextReader: React.FC = () => { - const { errorMessage, connect } = useWallet(); - return ( - <> -
Sign in
-
- {errorMessage} -
- - ); - }; - act(() => { - render( - - - , - ); - }); - await userEvent.click(screen.getByRole('sign-in')); - await waitFor(() => { - screen.getByText(errorMessageToDisplay); - }); - }); - - test('should provide active account', async () => { - const testActiveAccountId = 'account123'; - (setupWalletSelectorComponents as jest.Mock) - .mockResolvedValue({ - modal: 'foo', - selector: 'bar', - }); - (registerWalletAccountsSubscriber as jest.Mock) - .mockImplementation((callback) => { - callback([ - { active: false, accountId: 'fake.id' }, - { active: true, accountId: testActiveAccountId }, - ]); - return { - unsubscribe: jest.fn(), - }; - }); - (pollForWalletConnection as jest.Mock) - .mockResolvedValue(null); - - // ts claims this await is useless but it is needed. - const ContextReader: React.FC = () => { - const { activeAccountId } = useWallet(); - return ( -
{activeAccountId}
- ); - }; - - await act(async () => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - render( - - - , - ); - }); - await waitFor(() => { - screen.getByText(testActiveAccountId); - }); - }); + // test('should provide connection error message when polling times out', async () => { + // // throw on startup + // const errorMessageToDisplay = 'oh snap!'; + // (setupWalletSelectorComponents as jest.Mock) + // .mockResolvedValue({ + // modal: 'foo', + // selector: 'bar', + // }); + // (registerWalletAccountsSubscriber as jest.Mock) + // .mockImplementation((callback) => { + // callback(['whatever']); + // return { + // unsubscribe: jest.fn(), + // }; + // }); + // (pollForWalletConnection as jest.Mock) + // .mockRejectedValueOnce(new Error(errorMessageToDisplay)); + + // // eslint-disable-next-line @typescript-eslint/no-unused-vars + // const ContextReader: React.FC = () => { + // const { errorMessage, connect } = useMbWallet(); + // return ( + // <> + //
Sign in
+ //
+ // {errorMessage} + //
+ // + // ); + // }; + // act(() => { + // render( + // + // + // , + // ); + // }); + // await userEvent.click(screen.getByRole('sign-in')); + // await waitFor(() => { + // screen.getByText(errorMessageToDisplay); + // }); + // }); + + // test('should provide active account', async () => { + // const testActiveAccountId = 'account123'; + // (setupWalletSelectorComponents as jest.Mock) + // .mockResolvedValue({ + // modal: 'foo', + // selector: 'bar', + // }); + // (registerWalletAccountsSubscriber as jest.Mock) + // .mockImplementation((callback) => { + // callback([ + // { active: false, accountId: 'fake.id' }, + // { active: true, accountId: testActiveAccountId }, + // ]); + // return { + // unsubscribe: jest.fn(), + // }; + // }); + // (pollForWalletConnection as jest.Mock) + // .mockResolvedValue(null); + + // // ts claims this await is useless but it is needed. + // const ContextReader: React.FC = () => { + // const { activeAccountId } = useMbWallet(); + // return ( + //
{activeAccountId}
+ // ); + // }; + + // await act(async () => { + // // eslint-disable-next-line @typescript-eslint/no-unused-vars + // render( + // + // + // , + // ); + // }); + // await waitFor(() => { + // screen.getByText(testActiveAccountId); + // }); + // }); test('should provide event handlers', async () => { (setupWalletSelectorComponents as jest.Mock) @@ -168,7 +168,7 @@ describe('WalletContext', () => { .mockResolvedValue(['foo.near']); const ContextReader: React.FC = () => { - const { connect, disconnect } = useWallet(); + const { connect, disconnect } = useMbWallet(); return ( <>
Sign in
@@ -180,9 +180,9 @@ describe('WalletContext', () => { await act(async () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars render( - + - , + , ); }); diff --git a/packages/react/src/MintbaseWalletContext.tsx b/packages/react/src/MintbaseWalletContext.tsx index 6c9e39cda..593a40d97 100644 --- a/packages/react/src/MintbaseWalletContext.tsx +++ b/packages/react/src/MintbaseWalletContext.tsx @@ -13,8 +13,8 @@ import { pollForWalletConnection, signMessage, setupMintbaseWalletSelector, -} from '@mintbase-js/auth'; -import type { WalletSelectorComponents } from '@mintbase-js/auth'; +} from './wallet/wallet'; +import type { WalletSelectorComponents } from './wallet/wallet'; import type { WalletSelector, AccountState, @@ -23,8 +23,8 @@ import type { WalletModuleFactory, } from '@near-wallet-selector/core'; import type { WalletSelectorModal } from '@near-wallet-selector/modal-ui'; -import type { Network } from '@mintbase-js/sdk'; -import { mbjs } from '@mintbase-js/sdk'; +import '@near-wallet-selector/modal-ui/styles.css'; + // This is heavily based on // https://github.com/near/wallet-selector/blob/main/examples/react/contexts/WalletSelectorContext.tsx @@ -46,7 +46,7 @@ export type MintbaseWalletContext = { export const MintbaseWalletContext = createContext(null); // eslint-disable-next-line max-len -export const MintbaseWalletContextProvider: React.FC<{ children: React.ReactNode; callbackUrl: string; network?: Network; onlyMbWallet?: boolean; contractAddress?: string; additionalWallets?: Array }> = ({ +export const MintbaseWalletContextProvider: React.FC<{ children: React.ReactNode; callbackUrl?: string; network?: string; onlyMbWallet?: boolean; contractAddress?: string; additionalWallets?: Array }> = ({ children, network, contractAddress, additionalWallets, onlyMbWallet, callbackUrl, }): JSX.Element => { const [errorMessage, setErrorMessage] = useState(''); @@ -59,9 +59,9 @@ export const MintbaseWalletContextProvider: React.FC<{ children: React.ReactNode const [isWalletSelectorSetup, setIsWalletSelectorSetup] = useState(false); - const selectedNetwork = network || mbjs.keys.network; - const selectedContract = contractAddress || mbjs.keys.contractAddress; + const selectedNetwork = network; + const selectedContract = contractAddress; const setupMbWallet = async (): Promise => { const isOnlyMbWallet = !!onlyMbWallet || !!(additionalWallets && additionalWallets.length > 0); @@ -93,6 +93,8 @@ export const MintbaseWalletContextProvider: React.FC<{ children: React.ReactNode }; // call setup on wallet selector + + useEffect(() => { setupWallet(); diff --git a/packages/react/src/WalletContext.tsx b/packages/react/src/WalletContext.tsx deleted file mode 100644 index d14a4e3dd..000000000 --- a/packages/react/src/WalletContext.tsx +++ /dev/null @@ -1,184 +0,0 @@ -/* eslint-disable @typescript-eslint/explicit-function-return-type */ -import React, { - createContext, - useCallback, - useContext, - useEffect, - useMemo, - useState, -} from 'react'; -import { - registerWalletAccountsSubscriber, - setupWalletSelectorComponents, - connectWalletSelector, - disconnectFromWalletSelector, - pollForWalletConnection, - signMessage, -} from '@mintbase-js/auth/lib/wallet'; -import type { WalletSelectorComponents } from '@mintbase-js/auth/lib/wallet'; -import type { - WalletSelector, - AccountState, - VerifiedOwner, - VerifyOwnerParams, - WalletModuleFactory, -} from '@near-wallet-selector/core'; -import type { WalletSelectorModal } from '@near-wallet-selector/modal-ui'; -import type { Network } from '@mintbase-js/sdk'; -import { mbjs } from '@mintbase-js/sdk'; - -// This is heavily based on -// https://github.com/near/wallet-selector/blob/main/examples/react/contexts/WalletSelectorContext.tsx -// but uses wrappers from @mintbase-js/auth and @mintbase-js/sdk -export type WalletContext = { - selector: WalletSelector; - modal: WalletSelectorModal; - accounts: AccountState[]; - activeAccountId: string | null; - isConnected: boolean; - isWaitingForConnection: boolean; - isWalletSelectorSetup: boolean; - errorMessage: string | null; - connect: () => Promise; - disconnect: () => Promise; - signMessage: (params: VerifyOwnerParams) => Promise; -} - -export type WalletSetupComponents = { - selector: WalletSelector; - modal: WalletSelectorModal; -} - -export const WalletContext = createContext(null); - -export const WalletContextProvider: React.FC<{ children: React.ReactNode; network?: Network; contractAddress?: string; additionalWallets?: Array }> = ({ - children, network, contractAddress, additionalWallets, -}): JSX.Element => { - const [errorMessage, setErrorMessage] = useState(''); - const [components, setComponents] = useState( - null, - ); - const [accounts, setAccounts] = useState([]); - const [isWaitingForConnection, setIsWaitingForConnection] = - useState(false); - const [isWalletSelectorSetup, setIsWalletSelectorSetup] = - useState(false); - - const selectedNetwork = network || mbjs.keys.network; - const selectedContract = contractAddress || mbjs.keys.contractAddress; - - const setup = useCallback(async () => { - const components = await setupWalletSelectorComponents( - selectedNetwork, - selectedContract, - { - additionalWallets, - }, - ); - - setIsWalletSelectorSetup(true); - setComponents(components); - }, []); - - const onCloseModal = (): void => { - setIsWaitingForConnection(false); - }; - - const setupWallet = async () => { - const components = await setupWalletSelectorComponents( - selectedNetwork, - selectedContract, - { - additionalWallets, - }, - ); - return components; - }; - - // call setup on wallet selector - useEffect(() => { - setupWallet(); - - setup().catch((err: Error) => { - if (err || err.message.length > 0) { - setErrorMessage((err as Error).message); - } - }); - - // Add the event listener here - const closeButton = document?.getElementsByClassName('close-button')[0]; - closeButton?.addEventListener('click', onCloseModal); - - // Cleanup the event listener on unmount - return () => { - closeButton?.removeEventListener('click', onCloseModal); - }; - }, [setup]); - - // subscribe to account state changes - useEffect(() => { - if (!components) { - return undefined; - } - - const subscription = registerWalletAccountsSubscriber( - (accounts: AccountState[]) => { - setAccounts(accounts); - }, - ); - - return (): void => { - subscription.unsubscribe(); - }; - }, [components]); - - const { selector, modal } = components || {}; - - const connect = async (): Promise => { - setIsWaitingForConnection(true); - - setErrorMessage(null); - connectWalletSelector(); - - try { - const accounts = await pollForWalletConnection(); - setIsWaitingForConnection(false); - setAccounts(accounts); - } catch (err: unknown) { - if (err) { - setErrorMessage((err as Error).message); - } - } - }; - - const disconnect = async (): Promise => { - await disconnectFromWalletSelector(); - setIsWaitingForConnection(false); - }; - - const walletSelectorContextValue = useMemo( - () => ({ - selector: selector, - modal: modal, - accounts: accounts, - activeAccountId: - accounts.find((account) => account.active)?.accountId || null, - isConnected: accounts && accounts.length > 0, - isWaitingForConnection: isWaitingForConnection, - isWalletSelectorSetup: isWalletSelectorSetup, - errorMessage: errorMessage, - connect, - disconnect, - signMessage, - }), - [selector, modal, accounts], - ); - - return ( - - {children} - - ); -}; - -export const useWallet = (): WalletContext => useContext(WalletContext); diff --git a/packages/react/src/hooks/index.ts b/packages/react/src/hooks/index.ts index 41ca0ef4b..2610aa776 100644 --- a/packages/react/src/hooks/index.ts +++ b/packages/react/src/hooks/index.ts @@ -1,3 +1 @@ -// export * from './useMinter'; -export * from './useTokenById'; -export * from './useOwnedNftsByStores'; +export * from './useNearPrice'; diff --git a/packages/react/src/hooks/methods/constants.ts b/packages/react/src/hooks/methods/constants.ts new file mode 100644 index 000000000..c5c5b50f3 --- /dev/null +++ b/packages/react/src/hooks/methods/constants.ts @@ -0,0 +1,6 @@ +export const QUERY_OPS_PREFIX = 'mintbase_js_data_'; +export const META_SERVICE_HOST = 'https://api.mintbase.xyz'; +export const META_SERVICE_HOST_TESTNET = 'https://surface-api-testnet-z3w7d7dnea-ew.a.run.app'; +export const BINANCE_API = 'https://api.binance.com/api/v3/ticker/price?symbol=NEARUSDT'; +export const COIN_GECKO_API = 'https://api.coingecko.com/api/v3/simple/price?ids=near%2Cusn%2Cjumbo-exchange&include_last_updated_at=true&vs_currencies=usd%2Ceur%2Ccny'; +export const MINTBASE_API_KEY_HEADER = 'mb-api-key'; diff --git a/packages/react/src/hooks/methods/nearPrice.ts b/packages/react/src/hooks/methods/nearPrice.ts new file mode 100644 index 000000000..50cb429c7 --- /dev/null +++ b/packages/react/src/hooks/methods/nearPrice.ts @@ -0,0 +1,32 @@ +import { BINANCE_API, COIN_GECKO_API } from './constants'; +import { CoinGeckoNearPriceData, NearPriceData } from './nearPrice.types'; +import { ParsedDataReturn } from './types'; +import { parseData } from './utils'; + +export const fetchPriceFromCoinGecko = async (): Promise => { + const req = await fetch(COIN_GECKO_API); + const data = await req.json() as CoinGeckoNearPriceData; + return { price: data.near.usd }; +}; + +export const fetchPriceFromBinance = async (): Promise => { + const req = await fetch(BINANCE_API); + const data = await req.json() as NearPriceData; + return data; +}; + +export const nearPrice = async (): Promise> => { + try { + const res = await Promise.any([ + fetchPriceFromCoinGecko(), + fetchPriceFromBinance(), + ]); + + return parseData(res.price); + } catch (err) { + console.error( + `No price data was able to be fetched ${JSON.stringify(err.errors)}`, + ); + return parseData(null, err, err.message); + } +}; diff --git a/packages/react/src/hooks/methods/nearPrice.types.ts b/packages/react/src/hooks/methods/nearPrice.types.ts new file mode 100644 index 000000000..84df40a2e --- /dev/null +++ b/packages/react/src/hooks/methods/nearPrice.types.ts @@ -0,0 +1,9 @@ +export interface NearPriceData { + price?: string; +} +export interface CoinGeckoNearPriceData { + near?: { + usd: string; + }; +} + diff --git a/packages/react/src/hooks/methods/types.ts b/packages/react/src/hooks/methods/types.ts new file mode 100644 index 000000000..8347e4833 --- /dev/null +++ b/packages/react/src/hooks/methods/types.ts @@ -0,0 +1,5 @@ + +export interface ParsedDataReturn { + error?: null | string; + data?: T | null; +} diff --git a/packages/react/src/hooks/methods/utils.ts b/packages/react/src/hooks/methods/utils.ts new file mode 100644 index 000000000..7816e94dc --- /dev/null +++ b/packages/react/src/hooks/methods/utils.ts @@ -0,0 +1,10 @@ +import { ParsedDataReturn } from './types'; + +export const parseData = (data: T, error?: null | string, errorMsg?: string): ParsedDataReturn => { + if (error) { + console.error(errorMsg); + return { error: error }; + } + + return { data: data }; +}; diff --git a/packages/react/src/hooks/useNearPrice.ts b/packages/react/src/hooks/useNearPrice.ts index d4947f9d4..02548aa90 100644 --- a/packages/react/src/hooks/useNearPrice.ts +++ b/packages/react/src/hooks/useNearPrice.ts @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { nearPrice } from '@mintbase-js/data'; +import { nearPrice } from './methods/nearPrice'; type UseNearPriceReturn = { nearPrice: number; diff --git a/packages/react/src/hooks/useOwnedNftsByStores.ts b/packages/react/src/hooks/useOwnedNftsByStores.ts deleted file mode 100644 index 5a7b362a6..000000000 --- a/packages/react/src/hooks/useOwnedNftsByStores.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { useEffect, useState } from 'react'; -import { ownedNftsByStore } from '@mintbase-js/data'; -import { OwnedNftsData } from '@mintbase-js/data/lib/api/ownedNftsByStore/ownedNftsByStore.types'; -import { mbjs } from '@mintbase-js/sdk'; - -interface OwnedNftsByStoreHookResult { - data: OwnedNftsData | undefined; - error: string | null; - loading: boolean; -} - -export const useOwnedNftsByStore = ( - ownerId: string, - contractAddress: string = mbjs.keys.contractAddress, - pagination: { limit: number; offset?: number}, -): OwnedNftsByStoreHookResult => { - - const [loading, setLoading] = useState(true); - const [res, setData] = useState(undefined); - const [errorMsg, setError] = useState(null); - - const contract = contractAddress || mbjs.keys.contractAddress; - - - const validParams = contract && ownerId && pagination.limit; - - useEffect(() => { - let isCancelled = false; - - if (loading && validParams) { - (async (): Promise => { - const { data, error } = await ownedNftsByStore(ownerId, contract, pagination); - - if (error) { - setError(error as string); - setLoading(false); - } else if (data) { - setData(data); - setLoading(false); - } - })(); - } - - return (): void => { - isCancelled = true; - }; - }, [ownerId, pagination]); - - return { data: res, loading: loading, error: errorMsg }; -}; diff --git a/packages/react/src/hooks/useTokenById.ts b/packages/react/src/hooks/useTokenById.ts deleted file mode 100644 index 19bb33f04..000000000 --- a/packages/react/src/hooks/useTokenById.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { useEffect, useState } from 'react'; -import { tokenById } from '@mintbase-js/data'; -import { TokenByIdResults } from '@mintbase-js/data/lib/api/tokenById/tokenById.types'; - -interface TokenByIdHookResult { - data: TokenByIdResults | undefined; - error: string | null; - loading: boolean; -} - -export const useTokenById = ( - tokenId: string, - contractAddress: string, -): TokenByIdHookResult => { - const [loading, setLoading] = useState(true); - const [res, setData] = useState(undefined); - const [errorMsg, setError] = useState(null); - - const contract = contractAddress; - const validParams = contract && tokenId; - - - useEffect(() => { - let isCancelled = false; - - if (loading && validParams) { - (async (): Promise => { - const { data, error } = await tokenById(tokenId, contract); - - if (error) { - setError(error as string); - setLoading(false); - } else if (data) { - setData(data); - setLoading(false); - } - })(); - } - - return (): void => { - isCancelled = true; - }; - }, [tokenId, contractAddress]); - - return { data: res, loading: loading, error: errorMsg }; -}; diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index aa640ede4..4d5e22ad4 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -1,7 +1,2 @@ -export * from './WalletContext'; export * from './MintbaseWalletContext'; -// export * from './MintbaseSessionContext'; -// export * from './hooks/useMinter'; -// export * from './hooks/useTokenById'; -export * from './hooks/useOwnedNftsByStores'; export * from './hooks/useNearPrice'; diff --git a/packages/react/src/wallet/constants.ts b/packages/react/src/wallet/constants.ts new file mode 100644 index 000000000..3a2046b04 --- /dev/null +++ b/packages/react/src/wallet/constants.ts @@ -0,0 +1,9 @@ + +export const NEAR_LOGIN_CONTRACT_ID = process.env.NEAR_LOGIN_CONTRACT_ID; + +export const WALLET_CONNECTION_POLL_INTERVAL = 1_000; +// how long to wait for the user to make the connection to the wallet. +export const WALLET_CONNECTION_TIMEOUT = 30_000; + +export const MINTBASE_CONNECT_HOST = 'https://connect.mintbase.xyz'; +// export const MINTBASE_CONNECT_HOST = 'http://localhost:8000'; diff --git a/packages/react/src/wallet/wallet.ts b/packages/react/src/wallet/wallet.ts new file mode 100644 index 000000000..a4df056fa --- /dev/null +++ b/packages/react/src/wallet/wallet.ts @@ -0,0 +1,277 @@ +import { + setupWalletSelector, + VerifiedOwner, + VerifyOwnerParams, + Wallet, +} from '@near-wallet-selector/core'; +import { setupModal } from '@near-wallet-selector/modal-ui'; +import { map, distinctUntilChanged, Subscription } from 'rxjs'; + +import { + WALLET_CONNECTION_POLL_INTERVAL, + WALLET_CONNECTION_TIMEOUT, +} from './constants'; + +import { setupNearWallet } from '@near-wallet-selector/near-wallet'; +import { setupMeteorWallet } from '@near-wallet-selector/meteor-wallet'; +import { setupHereWallet } from '@near-wallet-selector/here-wallet'; +import { setupMyNearWallet } from '@near-wallet-selector/my-near-wallet'; + +import type { + WalletSelector, + AccountState, + WalletModuleFactory, +} from '@near-wallet-selector/core'; +import type { WalletSelectorModal } from '@near-wallet-selector/modal-ui'; +import { setupMintbaseWallet } from '@mintbase-js/wallet'; + +// error messages +const SUPPORT = '- further help available on our telegram channel: https://t.me/mintdev'; + +export const ERROR_MESSAGES = { + WALLET_SETUP_NOT_CALLED_ERROR : `Call and await setupWalletSelectorComponents() before registering a subscriber - ${SUPPORT}`, + WALLET_CONNECTION_NOT_FOUND: `Wallet connection not received after ${WALLET_CONNECTION_TIMEOUT}ms - ${SUPPORT}`, +}; + +export const SUPPORTED_NEAR_WALLETS: Array =[ + setupNearWallet(), + setupMeteorWallet(), + setupMyNearWallet(), + setupHereWallet(), +]; + +// mintbase SDK wallet functionality wraps +// Near Wallet Selector lib, provided by NEAR Protocol +// https://github.com/near/wallet-selector/ + +export type WalletSelectorComponents = { + selector: WalletSelector; + modal: WalletSelectorModal; +} + +// wallet components are held and exposed as a singleton reference +// this way they can be more easily passed to other components vs composing calls. +export let walletSelectorComponents: WalletSelectorComponents = { + selector: null, + modal: null, +}; + +/** + * Set up wallet selector components. Returns the modal + * See also docs on {@link https://github.com/near/wallet-selector/ | near wallet selector} + */ + +const walletUrls = { + testnet: 'https://testnet.wallet.mintbase.xyz/', + mainnet: 'https://wallet.mintbase.xyz', +}; + +// eslint-disable-next-line max-len +export const setupMintbaseWalletSelector = async ( + callbackUrl, + onlyMbWallet = false, + network?, + contractAddress?, + options?: { additionalWallets?: Array }, +): Promise => { + + + if (onlyMbWallet === false) { + + walletSelectorComponents.selector = await setupWalletSelector({ + network: network, + modules: [ + setupMintbaseWallet({ + networkId: network, + walletUrl: walletUrls[network], + deprecated: false, + callbackUrl: callbackUrl, + }), + ...(options?.additionalWallets || []), + ...SUPPORTED_NEAR_WALLETS, + ], + }); + } else { + walletSelectorComponents.selector = await setupWalletSelector({ + network: network, + modules: [ + setupMintbaseWallet({ + networkId: network, + walletUrl: walletUrls[network], + deprecated: false, + callbackUrl: callbackUrl, + }), + ...(options?.additionalWallets || []), + ], + }); + } + + walletSelectorComponents.modal = setupModal( walletSelectorComponents.selector, { + contractId: contractAddress, + }); + + return walletSelectorComponents; +}; + +export const setupWalletSelectorComponents = async ( + network?, + contractAddress?, + options?: { additionalWallets?: Array }, +): Promise => { + const selector = await setupWalletSelector({ + network: network, + modules: [ + ...SUPPORTED_NEAR_WALLETS, + ...(options?.additionalWallets || []), + ], + }); + + const modal = setupModal(selector, { + contractId: contractAddress, + }); + + walletSelectorComponents = { + selector, + modal, + }; + return walletSelectorComponents; +}; + +export class SetupNotCalledError extends Error { + message: string +} + +export class ConnectionTimeoutError extends Error { + message: string +} + +const validateWalletComponentsAreSetup = (): void => { + if (!walletSelectorComponents.selector) { + throw new SetupNotCalledError(ERROR_MESSAGES.WALLET_SETUP_NOT_CALLED_ERROR); + } +}; + +export const registerWalletAccountsSubscriber = ( + callback: (accounts: AccountState[]) => void, +): Subscription => { + validateWalletComponentsAreSetup(); + + return walletSelectorComponents.selector.store.observable + .pipe( + map((state) => state.accounts), + distinctUntilChanged(), + ) + .subscribe(callback); +}; + +// scoped to module and cleared since pollForWalletConnection might +// get called repeatedly in react enviroments +let timerReference = null; + +export const pollForWalletConnection = async (): Promise => { + validateWalletComponentsAreSetup(); + // clear any existing timer + clearTimeout(timerReference); + + const tryToResolveAccountsFromState = ( + resolve: (value: AccountState[]) => void, + reject: (err: ConnectionTimeoutError) => void, + elapsed = 0, + ): void => { + const { accounts } = + walletSelectorComponents.selector.store.getState() || {}; + + // accounts present in state + if (accounts) { + resolve(accounts); + } + + // timed out + if (elapsed > WALLET_CONNECTION_TIMEOUT) { + reject( + new ConnectionTimeoutError(ERROR_MESSAGES.WALLET_CONNECTION_NOT_FOUND), + ); + } + + // try again + clearTimeout(timerReference); + timerReference = setTimeout( + () => + tryToResolveAccountsFromState( + resolve, + reject, + elapsed + WALLET_CONNECTION_POLL_INTERVAL, + ), + WALLET_CONNECTION_POLL_INTERVAL, + ); + }; + + return new Promise((resolve, reject) => + tryToResolveAccountsFromState(resolve, reject), + ); +}; + +export const getWallet = async (): Promise => { + validateWalletComponentsAreSetup(); + + return await walletSelectorComponents.selector.wallet(); +}; + +export const connectWalletSelector = (): void => { + validateWalletComponentsAreSetup(); + + walletSelectorComponents.modal.show(); +}; + +export const disconnectFromWalletSelector = async (): Promise => { + validateWalletComponentsAreSetup(); + + const wallet = await walletSelectorComponents.selector.wallet(); + wallet.signOut(); +}; + +export const getVerifiedOwner = async ( + params: VerifyOwnerParams, +): Promise => { + validateWalletComponentsAreSetup(); + + const { message, callbackUrl, meta } = params; + + const wallet = await walletSelectorComponents.selector.wallet(); + + const owner = (await wallet.verifyOwner({ + message: message, + callbackUrl: callbackUrl, + meta: meta, + })) as VerifiedOwner; + + return owner; +}; + +// returns a signature of message +export const signMessage = async ( + params: VerifyOwnerParams, +): Promise => { + const owner = await getVerifiedOwner(params); + + return owner; +}; + + +// https://www.npmjs.com/package/bs58 +// https://github.com/feross/buffer +// https://github.com/near/wallet-selector/issues/434 +// export const verifyMessage = async (signature: string): Promise => { + +// // const owner = await getVerifiedOwner(signature); + +// // const publicKeyString = `ed25519:${BinaryToBase58(Buffer.from(owner.publicKey, 'base64'))}`; + +// // const createdPublicKey = utils.PublicKey.from(publicKeyString); + +// // const stringified = JSON.stringify(owner); + +// // const verified = createdPublicKey.verify(new Uint8Array(sha256.array(stringified)), Buffer.from(signature, 'base64')); + +// return false; +// }; diff --git a/packages/react/styleMock.js b/packages/react/styleMock.js new file mode 100644 index 000000000..746db6f66 --- /dev/null +++ b/packages/react/styleMock.js @@ -0,0 +1,2 @@ +// styleMock.js +module.exports = {}; \ No newline at end of file diff --git a/packages/rpc/package-lock.json b/packages/rpc/package-lock.json index a2e0c85ce..eddf9bb8f 100644 --- a/packages/rpc/package-lock.json +++ b/packages/rpc/package-lock.json @@ -1,20 +1,12 @@ { "name": "@mintbase-js/rpc", -<<<<<<< HEAD - "version": "0.0.5-add-issues-gh-action.1", -======= "version": "0.0.5-add-issues-gh-action.0", ->>>>>>> c0beb36 (add issues github actions script (#229)) "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@mintbase-js/rpc", -<<<<<<< HEAD - "version": "0.0.5-add-issues-gh-action.1", -======= "version": "0.0.5-add-issues-gh-action.0", ->>>>>>> c0beb36 (add issues github actions script (#229)) "license": "MIT", "dependencies": { "@types/node": "^16.0.0", diff --git a/packages/rpc/package.json b/packages/rpc/package.json index f9905621e..dd249d58d 100644 --- a/packages/rpc/package.json +++ b/packages/rpc/package.json @@ -21,7 +21,9 @@ "license": "MIT", "dependencies": { "@types/node": "^16.0.0", - "bn.js": "^5.2.1", + "bn.js": "^5.2.1" + }, + "peerDependencies": { "isomorphic-unfetch": "^3.1.0" }, "devDependencies": { diff --git a/packages/wallet/README.md b/packages/wallet/README.md index 25c808cf9..9b992594a 100644 --- a/packages/wallet/README.md +++ b/packages/wallet/README.md @@ -15,8 +15,7 @@ This is the [Mintbase Wallet](https://wallet.mintbase.xyz/) SDK package. You can check a quick example of Simple Login using Next.js 14 and @mintbase-js/react -

- +

@@ -123,39 +122,11 @@ const selector = await setupWalletSelector({ `networkId:` Near Networks - - - `walletUrl:` valid wallet urls - - - `successUrl:` If you dont have a single callback entrypoint to handle failure/success you can set successUrl. - - - `failureUrl:` If you dont have a single callback entrypoint to handle failure/success you can set failureUrl. - - - `callbackUrl:` when you have a single entrypoint to deal with transaction results. - - - -| property | value | - -|--|--| - -| networkId | `mainnet` or `testnet` | - -| walletUrl | `https://wallet.mintbase.xyz` or `https://testnet.wallet.mintbase.xyz` | - -| successUrl | any http or https url as a string| - -| failureUrl | any http or https url as a string| - -| callbackUrl | any http or https url as a string| ## Troubleshooting diff --git a/packages/wallet/src/mintbase-wallet.ts b/packages/wallet/src/mintbase-wallet.ts index bd9557a75..5d8da6c0d 100644 --- a/packages/wallet/src/mintbase-wallet.ts +++ b/packages/wallet/src/mintbase-wallet.ts @@ -1,6 +1,5 @@ -import * as nearAPI from 'near-api-js'; + import * as nearAPI from 'near-api-js'; -import BN from 'bn.js'; import type { Action, BrowserWallet, @@ -39,11 +38,15 @@ export type CallBackArgs = { type: TransactionSuccessEnum; } +interface Networks { + mainnet: string; + testnet: string; +} + export const MintbaseWallet: WalletBehaviourFactory< BrowserWallet, { - walletUrl: string; - networkId: string; + networkId: any; callback: string; successUrl?: string; failureUrl?: string; @@ -51,15 +54,12 @@ export const MintbaseWallet: WalletBehaviourFactory< > = async ({ metadata, options, - store, - logger, - emitter, - walletUrl, successUrl, failureUrl, callback, networkId, }) => { + const setupWalletState = async (): Promise | null => { if (typeof window !== undefined) { const { connect, WalletConnection, keyStores } = nearAPI; @@ -67,8 +67,8 @@ export const MintbaseWallet: WalletBehaviourFactory< const connectionConfig = { networkId: networkId, keyStore: new keyStores.BrowserLocalStorageKeyStore(), - nodeUrl: 'https://rpc.testnet.near.org', - walletUrl: walletUrl, + nodeUrl: options.network.nodeUrl, + walletUrl: metadata.walletUrl, headers: {}, }; @@ -162,7 +162,7 @@ export const MintbaseWallet: WalletBehaviourFactory< const stringifiedParam = JSON.stringify(transactions); const urlParam = encodeURIComponent(stringifiedParam); - const newUrl = new URL(`${walletUrl}/sign-transaction`); + const newUrl = new URL(`${metadata.walletUrl}/sign-transaction`); newUrl.searchParams.set('transactions_data', urlParam); newUrl.searchParams.set('callback_url', cbUrl); @@ -191,7 +191,7 @@ export const MintbaseWallet: WalletBehaviourFactory< const currentUrl = new URL(window.location.href); - const newUrl = new URL(`${walletUrl}/sign-transaction`); + const newUrl = new URL(`${metadata.walletUrl}/sign-transaction`); newUrl.searchParams.set('transactions_data', urlParam); if (successUrl) { @@ -210,13 +210,17 @@ export const MintbaseWallet: WalletBehaviourFactory< }; const verifyOwner = async (): Promise => { - console.error('mintbasewallet:verifyOwner is unsupported!'); + throw new Error(`The verifyOwner method is not supported by ${metadata.name}`); + }; - return; + const signMessage = async (): Promise => { + throw new Error(`The signMessage method is not supported by ${metadata.name}`); }; - const getAvailableBalance = async (): Promise => { - return new BN(0); + const getAvailableBalance = async (): Promise => { + // const accountId = state.wallet.getAccountId(); + // return await getBalance(accountId); + throw (`The getAvailableBalance method is not supported by ${metadata.name}`); }; const getAccounts = async (): Promise => { @@ -260,6 +264,7 @@ export const MintbaseWallet: WalletBehaviourFactory< signOut, signAndSendTransaction, verifyOwner, + signMessage, getAvailableBalance, getAccounts, switchAccount, diff --git a/packages/wallet/src/setup.ts b/packages/wallet/src/setup.ts index 33adc49f2..ec1c9830e 100644 --- a/packages/wallet/src/setup.ts +++ b/packages/wallet/src/setup.ts @@ -4,19 +4,29 @@ import type { WalletModule, } from '@near-wallet-selector/core'; import { MintbaseWallet } from './mintbase-wallet'; +import { resolveWalletUrl } from './utils'; + +interface MintbaseWalletSetup { + callbackUrl?: string; + successUrl?: string; + walletUrl?: string; + failureUrl?: string; + deprecated?: boolean; +} const icon = 'data:image/png;base64,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'; export function setupMintbaseWallet({ walletUrl = '', - networkId = '', deprecated = false, successUrl = '', failureUrl = '', callbackUrl = '', -}): WalletModuleFactory { +}: MintbaseWalletSetup = {}): WalletModuleFactory { + return async (moduleOptions): Promise | null> => { + const wallet: WalletModule = { id: 'mintbase-wallet', type: 'browser', @@ -29,10 +39,10 @@ export function setupMintbaseWallet({ available: true, successUrl, failureUrl, - walletUrl: walletUrl, + walletUrl: resolveWalletUrl(moduleOptions.options.network.networkId, walletUrl), }, init: (options) => { - return MintbaseWallet({ callback: callbackUrl, networkId, walletUrl, successUrl, failureUrl, ...options }); + return MintbaseWallet({ callback: callbackUrl, networkId: moduleOptions.options.network, successUrl, failureUrl, ...options }); }, }; return wallet; diff --git a/packages/wallet/src/utils.ts b/packages/wallet/src/utils.ts index dc5338c03..57e58ed1d 100644 --- a/packages/wallet/src/utils.ts +++ b/packages/wallet/src/utils.ts @@ -2,6 +2,8 @@ Mintbase Wallet Utils file */ +import { Network } from '@near-wallet-selector/core'; + const checkCallbackUrl = (callbackUrl: string): string => { function isValidURL(url): boolean { const urlPattern = /^(https?|ftp|http?):\/\/[^\s/$.?#].[^\s]*$/; @@ -66,4 +68,19 @@ const getCallbackUrl = (callbackUrl?: string): { cbUrl: string } | null => { return null; }; -export { checkCallbackUrl, getCallbackUrl }; +const resolveWalletUrl = (network: string, walletUrl?: string): string => { + if (walletUrl) { + return walletUrl; + } + + switch (network) { + case 'mainnet': + return 'https://wallet.mintbase.xyz'; + case 'testnet': + return 'https://testnet.wallet.mintbase.xyz/'; + default: + throw new Error('Invalid wallet url'); + } +}; + +export { checkCallbackUrl, getCallbackUrl, resolveWalletUrl };