init
This commit is contained in:
commit
99907ee706
8 changed files with 489 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
dist
|
||||
121
README.md
Normal file
121
README.md
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
|
||||
# ZanoWallet
|
||||
|
||||
`zano_web3` is a TypeScript library for interacting with the ZanoWallet extension in the browser. It allows you to connect to a user's ZanoWallet, handle authentication, and manage wallet credentials.
|
||||
|
||||
## Features
|
||||
|
||||
- **Easy Integration**: Simplifies the process of connecting to the ZanoWallet extension.
|
||||
- **Local Storage Support**: Optionally store wallet credentials in local storage.
|
||||
- **Customizable**: Offers hooks for various connection lifecycle events.
|
||||
- **Error Handling**: Provides a structured way to handle errors during the connection process.
|
||||
|
||||
## Installation
|
||||
|
||||
To install `zano_web3`, use npm or yarn:
|
||||
|
||||
```bash
|
||||
npm install zano_web3
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
yarn add zano_web3
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Importing the Library
|
||||
|
||||
```typescript
|
||||
import ZanoWallet from 'zano_web3';
|
||||
```
|
||||
|
||||
### Creating a ZanoWallet Instance
|
||||
|
||||
To create a `ZanoWallet` instance, you need to provide configuration options via the `ZanoWalletParams` interface.
|
||||
|
||||
```typescript
|
||||
const zanoWallet = new ZanoWallet({
|
||||
authPath: '/api/auth', // Custom server path for authentication
|
||||
useLocalStorage: true, // Store wallet credentials in local storage (default: true)
|
||||
aliasRequired: false, // Whether an alias is required (optional)
|
||||
customLocalStorageKey: 'myWalletKey', // Custom key for local storage (optional)
|
||||
customNonce: 'customNonceValue', // Custom nonce for signing (optional)
|
||||
disableServerRequest: false, // Disable server request after signing (optional)
|
||||
|
||||
onConnectStart: () => {
|
||||
console.log('Connecting to ZanoWallet...');
|
||||
},
|
||||
onConnectEnd: (data) => {
|
||||
console.log('Connected:', data);
|
||||
},
|
||||
onConnectError: (error) => {
|
||||
console.error('Connection error:', error);
|
||||
},
|
||||
beforeConnect: async () => {
|
||||
console.log('Preparing to connect...');
|
||||
},
|
||||
onLocalConnectEnd: (data) => {
|
||||
console.log('Local connection established:', data);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Connecting to ZanoWallet
|
||||
|
||||
To initiate the connection process, call the `connect` method:
|
||||
|
||||
```typescript
|
||||
await zanoWallet.connect();
|
||||
```
|
||||
|
||||
### Handling Wallet Credentials
|
||||
|
||||
You can manually manage wallet credentials using `getSavedWalletCredentials` and `setWalletCredentials` methods:
|
||||
|
||||
```typescript
|
||||
const credentials = zanoWallet.getSavedWalletCredentials();
|
||||
if (credentials) {
|
||||
console.log('Stored credentials:', credentials);
|
||||
}
|
||||
|
||||
zanoWallet.setWalletCredentials({
|
||||
nonce: 'newNonce',
|
||||
signature: 'newSignature',
|
||||
publicKey: 'newPublicKey'
|
||||
});
|
||||
```
|
||||
|
||||
## Using the `useZanoWallet` Hook
|
||||
|
||||
The `useZanoWallet` hook is a custom React hook provided by the `zano_web3` library. It simplifies the process of interacting with the ZanoWallet extension in a React application.
|
||||
|
||||
This hook is designed to handle server-side rendering (SSR) limitations by ensuring that it only runs on the client-side. This means that any code using the `useZanoWallet` hook will not be executed during server-side rendering, but will work as expected once the application is running in the browser.
|
||||
|
||||
To use the `useZanoWallet` hook, you can import it from the `zano_web3` library and call it within a functional component:
|
||||
|
||||
```typescript
|
||||
import { useZanoWallet } from 'zano_web3';
|
||||
|
||||
function MyComponent() {
|
||||
|
||||
const wallet = useZanoWallet({
|
||||
// same params as for new ZanoWallet
|
||||
});
|
||||
|
||||
return (
|
||||
<div>Your component...</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
- ZanoWallet browser extension must be installed.
|
||||
|
||||
## Contributing
|
||||
|
||||
If you find any issues or want to contribute, please create a pull request or submit an issue.
|
||||
6
index.ts
Normal file
6
index.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import zanoWallet from "./src/zanoWallet";
|
||||
|
||||
import {useZanoWallet} from "./src/hooks";
|
||||
export {useZanoWallet};
|
||||
|
||||
export default zanoWallet;
|
||||
102
package-lock.json
generated
Normal file
102
package-lock.json
generated
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
{
|
||||
"name": "zano_web3",
|
||||
"version": "2.3.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "zano_web3",
|
||||
"version": "2.3.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"react": "^18.3.1",
|
||||
"uuid": "^10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"typescript": "^5.5.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
|
||||
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz",
|
||||
"integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
|
||||
"integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"dependencies": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"loose-envify": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.5.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
|
||||
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
||||
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
36
package.json
Normal file
36
package.json
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"name": "zano_web3",
|
||||
"version": "2.3.0",
|
||||
"description": "",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/jejolare/zano_web3.git"
|
||||
},
|
||||
"keywords": [
|
||||
"zano",
|
||||
"web3",
|
||||
"crypto",
|
||||
"blockchain",
|
||||
"wallet"
|
||||
],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"typescript": "^5.5.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.3.1",
|
||||
"uuid": "^10.0.0"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/jejolare/zano_web3/issues"
|
||||
},
|
||||
"homepage": "https://github.com/jejolare/zano_web3#readme"
|
||||
}
|
||||
18
src/hooks.ts
Normal file
18
src/hooks.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import ZanoWallet, { ZanoWalletParams } from './zanoWallet';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
function useZanoWallet(params: ZanoWalletParams) {
|
||||
const [zanoWallet, setZanoWallet] = useState<ZanoWallet | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
setZanoWallet(new ZanoWallet(params));
|
||||
}, []);
|
||||
|
||||
return zanoWallet;
|
||||
}
|
||||
|
||||
export { useZanoWallet };
|
||||
192
src/zanoWallet.ts
Normal file
192
src/zanoWallet.ts
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export interface ZanoWalletParams {
|
||||
authPath: string;
|
||||
useLocalStorage?: boolean; // default: true
|
||||
aliasRequired?: boolean;
|
||||
customLocalStorageKey?: string;
|
||||
customNonce?: string;
|
||||
customServerPath?: string;
|
||||
disableServerRequest?: boolean;
|
||||
|
||||
onConnectStart?: (...params: any) => any;
|
||||
onConnectEnd?: (...params: any) => any;
|
||||
onConnectError?: (...params: any) => any;
|
||||
|
||||
beforeConnect?: (...params: any) => any;
|
||||
onLocalConnectEnd?: (...params: any) => any;
|
||||
}
|
||||
|
||||
type GlobalWindow = Window & typeof globalThis;
|
||||
|
||||
interface ZanoWindowParams {
|
||||
request: (str: string, params?: any, timeoutMs?: number | null) => Promise<any>;
|
||||
}
|
||||
|
||||
type ZanoWindow = Omit<GlobalWindow, 'Infinity'> & {
|
||||
zano: ZanoWindowParams
|
||||
}
|
||||
|
||||
interface WalletCredentials {
|
||||
nonce: string;
|
||||
signature: string;
|
||||
publicKey: string;
|
||||
}
|
||||
|
||||
class ZanoWallet {
|
||||
|
||||
private DEFAULT_LOCAL_STORAGE_KEY = "wallet";
|
||||
private localStorageKey: string;
|
||||
|
||||
private params: ZanoWalletParams;
|
||||
private zanoWallet: ZanoWindowParams;
|
||||
|
||||
constructor(params: ZanoWalletParams) {
|
||||
|
||||
if (typeof window === 'undefined') {
|
||||
throw new Error('ZanoWallet can only be used in the browser');
|
||||
}
|
||||
|
||||
if (!((window as unknown) as ZanoWindow).zano) {
|
||||
throw new Error('ZanoWallet requires the ZanoWallet extension to be installed');
|
||||
}
|
||||
|
||||
this.params = params;
|
||||
this.zanoWallet = ((window as unknown) as ZanoWindow).zano;
|
||||
this.localStorageKey = params.customLocalStorageKey || this.DEFAULT_LOCAL_STORAGE_KEY;
|
||||
}
|
||||
|
||||
|
||||
private handleError({ message } : { message: string }) {
|
||||
if (this.params.onConnectError) {
|
||||
this.params.onConnectError(message);
|
||||
} else {
|
||||
console.error(message);
|
||||
}
|
||||
}
|
||||
|
||||
getSavedWalletCredentials() {
|
||||
const savedWallet = localStorage.getItem(this.localStorageKey);
|
||||
if (!savedWallet) return undefined;
|
||||
try {
|
||||
return JSON.parse(savedWallet) as WalletCredentials;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
setWalletCredentials(credentials: WalletCredentials | undefined) {
|
||||
if (credentials) {
|
||||
localStorage.setItem(this.localStorageKey, JSON.stringify(credentials));
|
||||
} else {
|
||||
localStorage.removeItem(this.localStorageKey);
|
||||
}
|
||||
}
|
||||
|
||||
async connect() {
|
||||
|
||||
if (this.params.beforeConnect) {
|
||||
await this.params.beforeConnect();
|
||||
}
|
||||
|
||||
if (this.params.onConnectStart) {
|
||||
this.params.onConnectStart();
|
||||
}
|
||||
|
||||
const walletData = (await ((window as unknown) as ZanoWindow).zano.request('GET_WALLET_DATA')).data;
|
||||
|
||||
|
||||
if (!walletData?.address) {
|
||||
return this.handleError({ message: 'Companion is offline' });
|
||||
}
|
||||
|
||||
if (!walletData?.alias && this.params.aliasRequired) {
|
||||
return this.handleError({ message: 'Alias not found' });
|
||||
}
|
||||
|
||||
let nonce = "";
|
||||
let signature = "";
|
||||
let publicKey = "";
|
||||
|
||||
|
||||
const existingWallet = this.params.useLocalStorage ? this.getSavedWalletCredentials() : undefined;
|
||||
|
||||
if (existingWallet) {
|
||||
nonce = existingWallet.nonce;
|
||||
signature = existingWallet.signature;
|
||||
publicKey = existingWallet.publicKey;
|
||||
} else {
|
||||
const generatedNonce = this.params.customNonce || uuidv4();
|
||||
|
||||
const signResult = await this.zanoWallet.request(
|
||||
'REQUEST_MESSAGE_SIGN',
|
||||
{
|
||||
message: generatedNonce
|
||||
},
|
||||
null
|
||||
);
|
||||
|
||||
if (!signResult?.data?.result) {
|
||||
return this.handleError({ message: 'Failed to sign message' });
|
||||
}
|
||||
|
||||
nonce = generatedNonce;
|
||||
signature = signResult.data.result.sig;
|
||||
publicKey = signResult.data.result.pkey;
|
||||
}
|
||||
|
||||
|
||||
const serverData = {
|
||||
alias: walletData.alias,
|
||||
address: walletData.address,
|
||||
signature,
|
||||
publicKey,
|
||||
message: nonce,
|
||||
isSavedData: !!existingWallet
|
||||
}
|
||||
|
||||
if (this.params.onLocalConnectEnd) {
|
||||
this.params.onLocalConnectEnd(serverData);
|
||||
}
|
||||
|
||||
if (!this.params.disableServerRequest) {
|
||||
const result = await fetch( this.params.customServerPath || "/api/auth", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(
|
||||
{
|
||||
data: serverData
|
||||
}
|
||||
)
|
||||
})
|
||||
.then(res => res.json())
|
||||
.catch((e) => ({
|
||||
success: false,
|
||||
error: e.message
|
||||
}));
|
||||
|
||||
if (!result?.success || !result?.data) {
|
||||
return this.handleError({ message: result.error });
|
||||
}
|
||||
|
||||
if (!existingWallet && this.params.useLocalStorage) {
|
||||
this.setWalletCredentials({
|
||||
publicKey,
|
||||
signature,
|
||||
nonce
|
||||
});
|
||||
}
|
||||
|
||||
if (this.params.onConnectEnd) {
|
||||
this.params.onConnectEnd({
|
||||
...serverData,
|
||||
token: result.data.token
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ZanoWallet;
|
||||
12
tsconfig.json
Normal file
12
tsconfig.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES6",
|
||||
"module": "CommonJS",
|
||||
"declaration": true,
|
||||
"outDir": "./dist",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["ES6", "DOM"]
|
||||
},
|
||||
"include": ["src/**/*", "index.ts"]
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue