179 lines
4.9 KiB
TypeScript
179 lines
4.9 KiB
TypeScript
|
import { Wallet, ethers } from 'ethers'
|
||
|
import { stripHexPrefix } from 'ethereumjs-util'
|
||
|
import { getEncryptionPublicKey, decrypt } from 'eth-sig-util'
|
||
|
|
||
|
import { privateStorage } from '@/services'
|
||
|
import { SendRequestParams, Params } from '@/services/provider/@types'
|
||
|
|
||
|
import { RPC_LIST } from '@/constants'
|
||
|
|
||
|
import { Options } from './@types'
|
||
|
|
||
|
export const signerMethods = [
|
||
|
'net_version',
|
||
|
'eth_chainId',
|
||
|
'eth_decrypt',
|
||
|
'eth_accounts',
|
||
|
'eth_signTypedData',
|
||
|
'eth_requestAccounts',
|
||
|
'eth_getTransactionByHash',
|
||
|
'eth_getBalance',
|
||
|
'eth_getTransactionReceipt',
|
||
|
'eth_sendTransaction',
|
||
|
'eth_getEncryptionPublicKey',
|
||
|
]
|
||
|
|
||
|
export const localConnector = () => {
|
||
|
const wallet = privateStorage.get('local_provider')
|
||
|
|
||
|
if (!wallet) {
|
||
|
throw new Error('Key is not enough')
|
||
|
}
|
||
|
|
||
|
// TODO add validation
|
||
|
const { key, chainId } = wallet.data
|
||
|
|
||
|
return new LocalProvider({ key, chainId })
|
||
|
}
|
||
|
|
||
|
export class LocalProvider {
|
||
|
private readonly wallet: Wallet
|
||
|
private readonly provider: ethers.providers.JsonRpcProvider
|
||
|
|
||
|
public constructor({ key, chainId }: Options) {
|
||
|
this.wallet = new Wallet(key)
|
||
|
this.provider = new ethers.providers.JsonRpcProvider(RPC_LIST[chainId])
|
||
|
|
||
|
this.wallet = this.wallet.connect(this.provider)
|
||
|
}
|
||
|
|
||
|
public async request({ method, params }: SendRequestParams) {
|
||
|
switch (method) {
|
||
|
case 'eth_requestAccounts':
|
||
|
return await this.requestAccounts()
|
||
|
case 'eth_accounts':
|
||
|
return await this.accounts()
|
||
|
case 'eth_chainId':
|
||
|
return await this.chainId()
|
||
|
case 'eth_decrypt':
|
||
|
return this.decrypt(params as string[])
|
||
|
case 'eth_getEncryptionPublicKey':
|
||
|
return this.getEncryptionPublicKey()
|
||
|
case 'eth_getTransactionByHash':
|
||
|
return await this.getTransactionByHash(params as string[])
|
||
|
case 'eth_getBalance':
|
||
|
return await this.getBalance(params as string[])
|
||
|
case 'eth_getTransactionReceipt':
|
||
|
return await this.getTransactionReceipt(params as string[])
|
||
|
case 'net_version':
|
||
|
return await this.chainId()
|
||
|
case 'eth_sendTransaction':
|
||
|
return await this.sendTransaction(params as Params[])
|
||
|
default:
|
||
|
throw new Error('unsupported method')
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private decrypt([data]: string[]) {
|
||
|
try {
|
||
|
const stripped = stripHexPrefix(data)
|
||
|
const buff = Buffer.from(stripped, 'hex')
|
||
|
const msgData = JSON.parse(buff.toString('utf8'))
|
||
|
|
||
|
const privKey = stripHexPrefix(this.wallet.privateKey)
|
||
|
|
||
|
return decrypt(msgData, privKey)
|
||
|
} catch (err) {
|
||
|
throw new Error(`Provider method decrypt has error: ${err.message}`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private getEncryptionPublicKey() {
|
||
|
try {
|
||
|
return getEncryptionPublicKey(this.wallet.privateKey)
|
||
|
} catch (err) {
|
||
|
throw new Error(`Provider method getEncryptionPublicKey has error: ${err.message}`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private async requestAccounts() {
|
||
|
try {
|
||
|
const address = await this.wallet.getAddress()
|
||
|
return [address]
|
||
|
} catch (err) {
|
||
|
throw new Error(`Provider method requestAccounts has error: ${err.message}`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private async accounts() {
|
||
|
try {
|
||
|
const address = await this.wallet.getAddress()
|
||
|
return [address]
|
||
|
} catch (err) {
|
||
|
throw new Error(`Provider method accounts has error: ${err.message}`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private async chainId() {
|
||
|
try {
|
||
|
const network = await this.provider.getNetwork()
|
||
|
return network.chainId
|
||
|
} catch (err) {
|
||
|
throw new Error(`Provider method chainId has error: ${err.message}`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private async getTransactionByHash([txHash]: string[]) {
|
||
|
try {
|
||
|
return await this.provider.getTransaction(txHash)
|
||
|
} catch (err) {
|
||
|
throw new Error(`Provider method getTransactionByHash has error: ${err.message}`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private async getBalance([address]: string[]) {
|
||
|
try {
|
||
|
return await this.provider.getBalance(address)
|
||
|
} catch (err) {
|
||
|
throw new Error(`Provider method getBalance has error: ${err.message}`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private async getTransactionReceipt([txHash]: string[]) {
|
||
|
try {
|
||
|
return await this.provider.getTransactionReceipt(txHash)
|
||
|
} catch (err) {
|
||
|
throw new Error(`Provider method getTransactionReceipt has error: ${err.message}`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private async sendTransaction(params: Params[]) {
|
||
|
try {
|
||
|
const nonce = await this.wallet.getTransactionCount()
|
||
|
|
||
|
const [txParams] = params
|
||
|
|
||
|
// TODO validate
|
||
|
const newParams = {
|
||
|
nonce,
|
||
|
to: txParams.to,
|
||
|
from: txParams.from,
|
||
|
data: txParams.data,
|
||
|
value: txParams.value,
|
||
|
gasLimit: txParams.gas,
|
||
|
// ToDo EIP1559
|
||
|
gasPrice: txParams.gasPrice,
|
||
|
}
|
||
|
|
||
|
const signTx = await this.wallet.signTransaction(newParams)
|
||
|
const receipt = await this.provider.sendTransaction(signTx)
|
||
|
|
||
|
const { transactionHash } = await receipt.wait()
|
||
|
|
||
|
return transactionHash
|
||
|
} catch (err) {
|
||
|
throw new Error(`Provider method sendTransaction has error: ${err.message}`)
|
||
|
}
|
||
|
}
|
||
|
}
|