Initial commit

This commit is contained in:
Georges KABBOUCHI 2021-08-28 21:14:29 +03:00
commit 400a2d7581
14 changed files with 3752 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
# Library
node_modules
yarn-error.log
dist
.vscode
# OS
.DS_Store

6
.prettierrc Normal file
View File

@ -0,0 +1,6 @@
{
"singleQuote": true,
"trailingComma": "all",
"semi": false,
"arrowParens": "always"
}

1
README.md Normal file
View File

@ -0,0 +1 @@
# @kabbouchi/vue-web3

5
build.config.ts Normal file
View File

@ -0,0 +1,5 @@
import { defineBuildConfig } from 'unbuild'
export default defineBuildConfig({
entries: ['./src/index'],
})

10
jest.config.js Normal file
View File

@ -0,0 +1,10 @@
module.exports = {
preset: 'ts-jest',
globals: {
'ts-jest': {
diagnostics: {
warnOnly: true,
},
},
},
}

63
package.json Normal file
View File

@ -0,0 +1,63 @@
{
"name": "@kabbouchi/vue-web3",
"version": "0.1.0",
"description": "Vue web3 composition api",
"license": "MIT",
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/src/index.d.ts",
"scripts": {
"dev": "jiti scripts/watch.ts --cache",
"build": "unbuild",
"lint": "prettier -c --parser typescript \"{src,tests}/**/*.[jt]s?(x)\"",
"lint:fix": "yarn run lint --write",
"test:types": "tsc --build tsconfig.json",
"test:unit": "jest",
"test": "yarn run test:types && yarn run test:unit"
},
"files": [
"dist/**/*",
"LICENSE",
"README.md"
],
"dependencies": {
"@ethersproject/bytes": "^5.4.0",
"@ethersproject/keccak256": "^5.4.0",
"@web3-react/abstract-connector": "^6.0.7",
"@web3-react/types": "^6.0.7",
"tiny-invariant": "^1.1.0",
"vue-demi": "^0.11.3"
},
"devDependencies": {
"@types/jest": "^27.0.1",
"chokidar": "^3.5.2",
"jest": "^27.0.6",
"lint-staged": "^11.1.2",
"pascalcase": "^1.0.0",
"prettier": "^2.3.2",
"ts-jest": "^27.0.5",
"typescript": "^4.3.5",
"unbuild": "^0.4.2",
"vue": "^3.2.6"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^2.0.0 || >=3.0.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
},
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.js": [
"prettier --write"
],
"*.ts?(x)": [
"prettier --parser=typescript --write"
]
}
}

6
renovate.json Normal file
View File

@ -0,0 +1,6 @@
{
"extends": ["@nuxtjs"],
"lockFileMaintenance": {
"enabled": true
}
}

32
scripts/release.sh Executable file
View File

@ -0,0 +1,32 @@
#!/bin/bash
# Restore all git changes
git restore -s@ -SW -- example src test
# Resolve yarn
yarn
# Update token
if [[ ! -z ${NODE_AUTH_TOKEN} ]] ; then
echo "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" >> ~/.npmrc
echo "registry=https://registry.npmjs.org/" >> ~/.npmrc
echo "always-auth=true" >> ~/.npmrc
npm whoami
fi
# Get package name from package.json
PACKAGE_NAME=$(
cat package.json \
| grep name \
| head -1 \
| awk -F: '{ print $2 }' \
| sed 's/[",]//g'
)
# Release package
echo "🚀 Publishing$PACKAGE_NAME"
if npm publish -q --access public ; then
echo "✅ Published$PACKAGE_NAME"
else
echo "❌ Could'nt publish$PACKAGE_NAME"
fi

25
scripts/watch.ts Normal file
View File

@ -0,0 +1,25 @@
import { watch } from 'chokidar'
import { build } from 'unbuild'
import { resolve } from 'upath'
import consola from 'consola'
// Package root
const rootDir = resolve(__dirname, '..')
// Package src
const src = resolve(__dirname, '../src')
// Package build promise
const tryBuild = async () => {
try {
await build(rootDir, false)
} catch (e) {
consola.log(e)
} finally {
consola.info('Waiting for changes...')
}
}
// Watch src, rebuild on any change
watch(src).on('change', tryBuild)
watch(src).on('add', tryBuild)
watch(src).on('unlink', tryBuild)

125
src/index.ts Normal file
View File

@ -0,0 +1,125 @@
import { AbstractConnector } from '@web3-react/abstract-connector'
import { normalizeAccount, normalizeChainId } from './normalizers'
import { ConnectorUpdate } from '@web3-react/types'
import { computed, ref, watch } from 'vue-demi'
class UnsupportedChainIdError extends Error {
public constructor(
unsupportedChainId: number,
supportedChainIds?: readonly number[],
) {
super()
this.name = this.constructor.name
this.message = `Unsupported chain id: ${unsupportedChainId}. Supported chain ids are: ${supportedChainIds}.`
}
}
const connector = ref<AbstractConnector>()
const chainId = ref()
const account = ref<null | string>()
const provider = ref<any>()
const error = ref<Error>()
const active = computed(
() =>
connector.value !== undefined &&
chainId.value !== undefined &&
account.value !== undefined &&
!!!error.value,
)
const library = ref()
let getLibrary = (provider?: any, connector?: any) => () => null
export const setWeb3LibraryCallback = (
cb: (provider?: any, connector?: any) => any,
) => {
getLibrary = cb
}
export const useWeb3 = () => {
const activate = async (
c: AbstractConnector,
onError?: (error: Error) => void,
throwErrors: boolean = false,
) => {
let activated = false
try {
const update = await c.activate().then((update) => {
activated = true
return update
})
const augmentedUpdate = await augmentConnectorUpdate(c, update)
connector.value = c
chainId.value = augmentedUpdate.chainId
provider.value = augmentedUpdate.provider
account.value = augmentedUpdate.account
} catch (e) {
error.value = e
if (throwErrors) {
activated && c.deactivate()
throw e
} else if (onError) {
activated && c.deactivate()
onError(e)
}
}
}
const deactivate = () => {
connector.value?.deactivate()
}
watch([active, provider, connector, chainId], () => {
library.value =
active.value &&
chainId.value !== undefined &&
Number.isInteger(chainId.value) &&
!!connector.value
? getLibrary(provider.value, connector.value)
: undefined
})
return {
library,
active,
activate,
deactivate,
connector,
chainId,
account,
provider,
error,
}
}
async function augmentConnectorUpdate(
connector: AbstractConnector,
update: ConnectorUpdate,
): Promise<ConnectorUpdate<number>> {
const provider =
update.provider === undefined
? await connector.getProvider()
: update.provider
const [_chainId, _account] = (await Promise.all([
update.chainId === undefined ? connector.getChainId() : update.chainId,
update.account === undefined ? connector.getAccount() : update.account,
])) as [
Required<ConnectorUpdate>['chainId'],
Required<ConnectorUpdate>['account'],
]
const chainId = normalizeChainId(_chainId)
if (
!!connector.supportedChainIds &&
!connector.supportedChainIds.includes(chainId)
) {
throw new UnsupportedChainIdError(chainId, connector.supportedChainIds)
}
const account = _account === null ? _account : normalizeAccount(_account)
return { provider, chainId, account }
}

63
src/normalizers.ts Normal file
View File

@ -0,0 +1,63 @@
import { arrayify } from '@ethersproject/bytes'
import { keccak256 } from '@ethersproject/keccak256'
import invariant from 'tiny-invariant'
export function normalizeChainId(chainId: string | number): number {
if (typeof chainId === 'string') {
// Temporary fix until the next version of Metamask Mobile gets released.
// In the current version (0.2.13), the chainId starts with “Ox” rather
// than “0x”. Fix: https://github.com/MetaMask/metamask-mobile/pull/1275
chainId = chainId.replace(/^Ox/, '0x')
const parsedChainId = Number.parseInt(
chainId,
chainId.trim().substring(0, 2) === '0x' ? 16 : 10,
)
invariant(
!Number.isNaN(parsedChainId),
`chainId ${chainId} is not an integer`,
)
return parsedChainId
} else {
invariant(Number.isInteger(chainId), `chainId ${chainId} is not an integer`)
return chainId
}
}
// https://github.com/ethers-io/ethers.js/blob/d9d438a119bb11f8516fc9cf02c534ab3816fcb3/packages/address/src.ts/index.ts
export function normalizeAccount(_address: string): string {
invariant(
typeof _address === 'string' && _address.match(/^(0x)?[0-9a-fA-F]{40}$/),
`Invalid address ${_address}`,
)
const address = _address.substring(0, 2) === '0x' ? _address : `0x${_address}`
const chars = address.toLowerCase().substring(2).split('')
const charsArray = new Uint8Array(40)
for (let i = 0; i < 40; i++) {
charsArray[i] = chars[i].charCodeAt(0)
}
const hashed = arrayify(keccak256(charsArray))
for (let i = 0; i < 40; i += 2) {
if (hashed[i >> 1] >> 4 >= 8) {
chars[i] = chars[i].toUpperCase()
}
if ((hashed[i >> 1] & 0x0f) >= 8) {
chars[i + 1] = chars[i + 1].toUpperCase()
}
}
const addressChecksum = `0x${chars.join('')}`
invariant(
!(
address.match(/([A-F].*[a-f])|([a-f].*[A-F])/) &&
address !== addressChecksum
),
`Bad address checksum ${address} ${addressChecksum}`,
)
return addressChecksum
}

3
tests/index.spec.ts Normal file
View File

@ -0,0 +1,3 @@
describe('useWeb3', () => {
it('returns default value', () => {})
})

31
tsconfig.json Normal file
View File

@ -0,0 +1,31 @@
{
"include": ["scripts/watch.ts", "src/**/*", "tests/**/*"],
"exclude": ["dist", "node_modules"],
"compilerOptions": {
"rootDir": ".",
"outDir": "dist",
"sourceMap": false,
"noEmit": true,
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"skipLibCheck": true,
"noUnusedLocals": true,
"strictNullChecks": true,
"noImplicitAny": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"strict": true,
"isolatedModules": false,
"experimentalDecorators": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"removeComments": false,
"lib": ["esnext"],
"types": ["node", "jest"]
}
}

3375
yarn.lock Normal file

File diff suppressed because it is too large Load Diff