fix: update

This commit is contained in:
Nik Dement'ev 2020-11-06 00:10:04 +03:00
commit 2f0fbf56f0
No known key found for this signature in database
GPG Key ID: 769B05D57CF16FE2
14 changed files with 12097 additions and 0 deletions

10
.eslintrc.js Normal file
View File

@ -0,0 +1,10 @@
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint',
sourceType: 'module'
},
extends: [
'@nuxtjs'
]
}

View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/provider.iml" filepath="$PROJECT_DIR$/.idea/provider.iml" />
</modules>
</component>
</project>

12
.idea/provider.iml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

55
.idea/workspace.xml Normal file
View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="c0d2cb1a-45f7-4ae6-a3c7-4649a212b52b" name="Default Changelist" comment="">
<change afterPath="$PROJECT_DIR$/.eslintrc.js" afterDir="false" />
<change afterPath="$PROJECT_DIR$/babel.config.js" afterDir="false" />
<change afterPath="$PROJECT_DIR$/lib/module.js" afterDir="false" />
<change afterPath="$PROJECT_DIR$/lib/plugin.js" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nuxt.config.js" afterDir="false" />
<change afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
<change afterPath="$PROJECT_DIR$/types/index.d.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/types/vue.d.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/yarn.lock" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProjectId" id="1jsuBfSpRzpsyM93w9WXPUOUpVQ" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="nodejs_package_manager_path" value="yarn" />
<property name="ts.external.directory.path" value="$APPLICATION_HOME_DIR$/plugins/JavaScriptLanguage/jsLanguageServicesImpl/external" />
<property name="vue.rearranger.settings.migration" value="true" />
</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$" />
</key>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="c0d2cb1a-45f7-4ae6-a3c7-4649a212b52b" name="Default Changelist" comment="" />
<created>1604604985514</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1604604985514</updated>
<workItem from="1604604986572" duration="1150000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
</project>

11
babel.config.js Normal file
View File

@ -0,0 +1,11 @@
module.exports = {
presets: [
[
'@babel/preset-env', {
targets: {
esmodules: true
}
}
]
]
}

21
lib/module.js Normal file
View File

@ -0,0 +1,21 @@
const { resolve } = require('path')
module.exports = async function module(moduleOptions) {
const defaultOptions = {
id: 1,
rpcUrl: '',
rpcCallRetryAttempt: 15,
blockGasLimit: 7300000
}
const options = Object.assign({}, defaultOptions, this.options.provider, moduleOptions)
// Register plugin
this.addPlugin({
src: resolve(__dirname, 'plugin.js'),
fileName: 'provider.js',
options
})
}
module.exports.meta = require('../package.json')

301
lib/plugin.js Normal file
View File

@ -0,0 +1,301 @@
const Web3 = require('web3')
const { hexToNumberString, toChecksumAddress, numberToHex } = require('web3-utils')
export default (ctx, inject) => {
const moduleOptions = <%= JSON.stringify(options) %>
const instance = class Provider {
constructor(options) {
this.address = ''
this.version = 'new'
this.config = options.config
this.web3 = new Web3(new Web3.providers.HttpProvider(options.rpcUrl))
}
async initProvider(provider) {
try {
this.provider = provider
this.web3 = new Web3(provider)
await this._checkVersion()
return await this._initProvider()
} catch (err) {
throw new Error(`Provider method initProvider has error: ${err.message}`)
}
}
async sendRequest(params) {
try {
const request = args =>
this.version === 'old' ? this._sendAsync(args) : this.provider.request(args)
return await request(params)
} catch (err) {
throw new Error(`Provider method sendRequest has error: ${err.message}`)
}
}
getContract({ abi, address }) {
return new this.web3.eth.Contract(abi, address)
}
async deployContract({ domain, deploymentActions, abi, address }) {
try {
const [{ bytecode, expectedAddress }] = deploymentActions.actions.filter(
action => action.domain === domain
)
const code = await this.web3.eth.getCode(expectedAddress)
if (code !== '0x') {
throw new Error('Already deployed')
}
const data = this.getContract({ abi, address })
.methods.deploy(bytecode, deploymentActions.salt)
.encodeABI()
const callParams = {
method: 'eth_sendTransaction',
params: [
{
from: this.address,
to: this.getContract()._address,
gas: numberToHex(6e6),
gasPrice: '0x100000000',
value: 0,
data
}
],
from: this.address
}
return await this.sendRequest(callParams)
} catch (err) {
throw new Error(`Provider method deployContract has error: ${err.message}`)
}
}
async contractRequest({ methodName, data, to, from, gas, value = 0 }) {
const { rpcCallRetryAttempt, blockGasLimit } = this.config
try {
const params = {
to,
data,
value,
from: from || this.address,
gas: gas || blockGasLimit + 100000
}
return await this._repeatUntilResult(
() => this.web3.eth[methodName](params),
rpcCallRetryAttempt
)
} catch (err) {
throw new Error(`Provider method contractRequest has error: ${err.message}`)
}
}
async getBalance({ address }) {
const { rpcCallRetryAttempt } = this.config
try {
const params = {
method: 'eth_getBalance',
params: [address, 'latest']
}
const balance = await this._repeatUntilResult(
() => this.sendRequest(params),
rpcCallRetryAttempt
)
return hexToNumberString(balance)
} catch (err) {
throw new Error(`Provider method getBalance has error: ${err.message}`)
}
}
async waitForTxReceipt({ txHash }) {
const { rpcCallRetryAttempt } = this.config
try {
const params = {
method: 'eth_getTransactionReceipt',
params: [txHash]
}
return await this._repeatUntilResult(() => this.sendRequest(params), rpcCallRetryAttempt * 10)
} catch (err) {
throw new Error(`Provider method waitForTxReceipt has error: ${err.message}`)
}
}
async batchRequest({ txs, callback }) {
try {
const txsPromisesBucket = []
for (const [index, params] of txs.entries()) {
const txPromise = this.sendRequest({
method: 'eth_sendTransaction',
params: [params]
})
await this._sleep(1000)
if (!(index % 2) && index !== 0) {
await txPromise
}
txsPromisesBucket.push(txPromise)
}
callback(txsPromisesBucket)
return await Promise.all(txsPromisesBucket)
} catch (err) {
throw new Error(err.message)
}
}
async checkNetworkVersion() {
try {
return await this.sendRequest({ method: 'net_version' })
} catch (err) {
throw new Error(`Provider method _checkNetworkVersion has error: ${err.message}`)
}
}
on({ method, callback }) {
try {
this.provider.on(method, callback)
} catch (err) {
throw new Error(`Provider method _checkNetworkVersion has error: ${err.message}`)
}
}
async _initProvider() {
try {
const request = () =>
this.version ? this.provider.enable() : this.sendRequest({ method: 'eth_requestAccounts' })
const [account] = await request('')
if (!account) {
throw new Error('Locked metamask')
}
this.address = account
this.provider.on('accountsChanged', accounts => this._onAccountsChanged(accounts))
this.provider.on('chainChanged', id => this._onNetworkChanged({ id }))
this.config.id = await this.checkNetworkVersion()
return toChecksumAddress(account)
} catch (err) {
throw new Error(`Provider method _initProvider has error: ${err.message}`)
}
}
_sleep(time) {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, time)
})
}
_sendAsync({ method, params, from }) {
const { id } = this.config
switch (id) {
case 77:
case 99:
case 100:
from = undefined
break
}
return new Promise((resolve, reject) => {
const callback = (err, response) => {
if (err || response.error) {
reject(err)
}
resolve(response.result)
}
this.provider.sendAsync(
{
method,
params,
jsonrpc: '2.0',
from
},
callback
)
})
}
async _send({ method, params }) {
try {
return await this.provider.send(method, params)
} catch (err) {
throw new Error(`Provider method _send has error: ${err.message}`)
}
}
_onNetworkChanged({ id }) {
if (id) {
this.network = id
window.location.reload()
}
}
_onAccountsChanged(accounts) {
const [account] = accounts
if (account) {
this.address = toChecksumAddress(account)
window.location.reload()
}
}
_checkVersion() {
if (this.provider && this.provider.request) {
this.version = 'new'
} else {
this.version = 'old'
}
}
_repeatUntilResult(action, totalAttempts, retryAttempt = 1) {
return new Promise((resolve, reject) => {
const iteration = async () => {
const result = await action()
if (!result) {
if (retryAttempt <= totalAttempts) {
retryAttempt++
setTimeout(iteration, 1000 * retryAttempt)
} else {
return reject(new Error('Tx not minted'))
}
} else {
resolve(result)
}
}
iteration()
})
}
}
const provider = new instance({ config: moduleOptions })
inject('provider', provider)
ctx.$provider = provider
}

5
nuxt.config.js Normal file
View File

@ -0,0 +1,5 @@
export default {
modules: [
['@nuxtjs/provider']
],
}

32
package.json Normal file
View File

@ -0,0 +1,32 @@
{
"name": "@nuxtjs/provider",
"version": "0.0.1",
"description": "Provider integration with Nuxt.js",
"repository": "provider-module",
"license": "MIT",
"contributors": "nikdementev",
"main": "lib/module.js",
"types": "types/index.d.ts",
"files": [
"lib",
"types/*.d.ts"
],
"scripts": {
"lint": "eslint lib test",
"release": "standard-version && git push --follow-tags && npm publish"
},
"dependencies": {
"web3": "^1.3.0",
"web3-utils": "^1.3.0"
},
"devDependencies": {
"@babel/core": "latest",
"@babel/preset-env": "latest",
"@nuxtjs/eslint-config": "latest",
"babel-eslint": "latest",
"babel-jest": "latest",
"eslint": "latest",
"nuxt-edge": "latest",
"standard-version": "latest"
}
}

94
types/index.d.ts vendored Normal file
View File

@ -0,0 +1,94 @@
import Vue from 'vue'
import Web3 from 'web3'
import './vuex'
type Address = string
type Params = {
to: string
gas: string
from: string
data: string
value: string
gasPrice: string
}
type RequestParams = {
method: string
params?: Params[] | object;
}
type ContractRequestParams = {
to: string
gas: string
from: string
methodName: string
data?: string
value?: number
}
export type TransactionStatus =
| 'success'
| 'fail'
| 'pending'
export interface TransactionReceipt {
transactionResult: unknown
status: TransactionStatus
transactionError?: string
}
type GetBalanceParams = {
address: string
}
type WaitForTxReceiptParams = {
address: string
}
type BatchRequestParams = {
txs: Params[]
callback?: (params: Promise<string>[]) => void
}
type OnListenerParams = {
method: string
callback: CallableFunction
}
interface ProviderOptions {
id?: string,
rpcUrl?: string,
rpcCallRetryAttempt?: number,
blockGasLimit?: number,
}
interface ProviderInstance {
readonly web3: typeof Web3
readonly config: ProviderOptions
initProvider(provider: unknown): Promise<Address>
sendRequest(params: RequestParams): Promise<TransactionReceipt>
contractRequest(params: ContractRequestParams): Promise<TransactionReceipt>
getBalance(params: GetBalanceParams): Promise<number>
waitForTxReceipt(params: WaitForTxReceiptParams): Promise<TransactionReceipt>
batchRequest(params: WaitForTxReceiptParams): Promise<string[]>
checkNetworkVersion(params: WaitForTxReceiptParams): Promise<string>
on(params: OnListenerParams): void
}
declare module '@nuxt/vue-app' {
interface Context {
$axios: ProviderInstance
}
interface NuxtAppOptions {
$axios: ProviderInstance
}
}
declare module 'vue/types/vue' {
interface Vue {
$provider: ProviderInstance
}
}

7
types/vue.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
import { ProviderInstance } from '.'
declare module 'vuex/types/index' {
interface Store<S> {
$provider: ProviderInstance,
}
}

11530
yarn.lock Normal file

File diff suppressed because it is too large Load Diff