Skip to content

Commit

Permalink
Merge pull request #160 from consensusnetworks/feature/ssv-deposit-ba…
Browse files Browse the repository at this point in the history
…lance

Feature/ssv deposit balance
  • Loading branch information
ccali11 committed Oct 21, 2022
2 parents 6cba763 + fef244b commit 3cd0add
Show file tree
Hide file tree
Showing 26 changed files with 18,021 additions and 14,314 deletions.
6 changes: 2 additions & 4 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,13 @@
},
"dependencies": {
"@heroicons/vue": "^1.0.6",
"@ledgerhq/hw-app-eth": "^6.29.4",
"@ledgerhq/hw-transport-webusb": "^6.27.4",
"@solana/web3.js": "^1.63.1",
"@walletconnect/client": "^1.8.0",
"@walletconnect/qrcode-modal": "^1.8.0",
"@walletconnect/web3-provider": "^1.8.0",
"borsh": "^0.7.0",
"buffer": "^6.0.3",
"ethers": "^5.6.9",
"iotex-antenna": "^0.31.3",
"util": "^0.12.5",
"vue": "^3.2.25",
"vue-router": "^4.0.15"
},
Expand Down
57 changes: 56 additions & 1 deletion apps/web/src/components/Wallet.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
<template>
<div>
<div class="staking-container">
<button @click="getPoolsForUser">
What do I have staked where?
</button>
<ul>
<li
v-for="(pool, index) in pools"
:key="index"
>
<p>Pool Address: {{ pool.address }}</p>
<p>User Balance: {{ pool.userBalance }} ETH</p>
<p>Total Pool Balance: {{ pool.balance }} ETH</p>
</li>
</ul>
<input
v-model="amountToStake"
placeholder="Amount to Stake"
>
<button @click="deposit">
Steak
</button>
</div>
<div class="connect-wallet-container">
<div class="metamask-div">
<button @click="connectWallet('MetaMask')">
Expand Down Expand Up @@ -63,8 +85,12 @@
class="wallet-connect-btn"
@click="connectWallet('WalletConnect')"
>
WalletConnect
{{ walletConnectButtonText }}
</button>
<p>
Connected WalletConnect Account:
<span> {{ walletConnectAccountsResult }} </span>
</p>
</div>
</div>
<div class="form-container">
Expand Down Expand Up @@ -118,15 +144,21 @@ const phantomButtonText = ref<string>('Connect Phantom')
const phantomAccountsResult = ref<string>('Address Not Active')
const ledgerButtonText = ref<string>('Connect Ledger')
const ledgerAccountsResult = ref<string>('Address Not Active')
const walletConnectButtonText = ref<string>('Connect WalletConnect')
const walletConnectAccountsResult = ref<string>('Address Not Active')
const {
selectedProvider,
selectedAccount,
toAddress,
amount,
amountToStake,
pools,
connectWallet,
sendTransaction,
signMessage,
getPoolsForUser,
deposit
} = useWallet()
watchEffect(() => {
Expand All @@ -141,6 +173,8 @@ watchEffect(() => {
phantomAccountsResult.value = 'Not Active'
ledgerButtonText.value = 'Connect Ledger'
ledgerAccountsResult.value = 'Not Active'
walletConnectButtonText.value = 'Connect WalletConnect'
walletConnectAccountsResult.value = 'Not Active'
} else if (selectedProvider.value === 'CoinbaseWallet') {
metamaskButtonText.value = 'Connect Metamask'
coinbaseButtonText.value = 'Coinbase Connected'
Expand All @@ -152,6 +186,8 @@ watchEffect(() => {
phantomAccountsResult.value = 'Not Active'
ledgerButtonText.value = 'Connect Ledger'
ledgerAccountsResult.value = 'Not Active'
walletConnectButtonText.value = 'Connect WalletConnect'
walletConnectAccountsResult.value = 'Not Active'
} else if (selectedProvider.value === 'IoPay') {
metamaskButtonText.value = 'Connect MetaMask'
coinbaseButtonText.value = 'Connect Coinbase'
Expand All @@ -163,6 +199,8 @@ watchEffect(() => {
phantomAccountsResult.value = 'Not Active'
ledgerButtonText.value = 'Connect Ledger'
ledgerAccountsResult.value = 'Not Active'
walletConnectButtonText.value = 'Connect WalletConnect'
walletConnectAccountsResult.value = 'Not Active'
} else if (selectedProvider.value === 'Phantom') {
metamaskButtonText.value = 'Connect MetaMask'
coinbaseButtonText.value = 'Connect Coinbase'
Expand All @@ -174,6 +212,8 @@ watchEffect(() => {
phantomAccountsResult.value = selectedAccount.value || 'Not Active'
ledgerButtonText.value = 'Connect Ledger'
ledgerAccountsResult.value = 'Not Active'
walletConnectButtonText.value = 'Connect WalletConnect'
walletConnectAccountsResult.value = 'Not Active'
} else if (selectedProvider.value === 'Ledger') {
metamaskButtonText.value = 'Connect MetaMask'
coinbaseButtonText.value = 'Connect Coinbase'
Expand All @@ -185,6 +225,21 @@ watchEffect(() => {
phantomAccountsResult.value = 'Not Active'
ledgerButtonText.value = 'Connected!'
ledgerAccountsResult.value = selectedAccount.value
walletConnectButtonText.value = 'Connect WalletConnect'
walletConnectAccountsResult.value = 'Not Active'
} else if (selectedProvider.value === 'WalletConnect') {
metamaskButtonText.value = 'Connect MetaMask'
coinbaseButtonText.value = 'Connect Coinbase'
ioPayButtonText.value = 'Connect ioPay'
phantomButtonText.value = 'Connected'
metamaskAccountsResult.value = 'Not Active'
coinbaseAccountsResult.value = 'Not Active'
ioPayAccountsResult.value = 'Not Active'
phantomAccountsResult.value = 'Not Active'
ledgerButtonText.value = 'Connect Ledger'
ledgerAccountsResult.value = 'Not Active'
walletConnectButtonText.value = 'Connected!'
walletConnectAccountsResult.value = selectedAccount.value
}
})
</script>
Expand Down
12 changes: 12 additions & 0 deletions apps/web/src/composables/environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default function useEnvironment() {
const ethereumURL = import.meta.env.PUBLIC_ETHEREUM_RPC || 'http://127.0.0.1:8545/'
const ledgerType = import.meta.env.PUBLIC_SPECULOS_PORT ? 'speculos' : 'usb'
const speculosURL = import.meta.env.PUBLIC_SPECULOS_PORT ? `http://127.0.0.1:${import.meta.env.PUBLIC_SPECULOS_PORT}` : undefined
const walletConnectURL = 'https://bridge.walletconnect.org'
return {
ethereumURL,
ledgerType,
speculosURL,
walletConnectURL
}
}
22 changes: 13 additions & 9 deletions apps/web/src/composables/ethers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { EthersProvider } from '@/interfaces/EthersProvider'
import { ProviderString } from '@/types/ProviderString'
import { TransactionInit } from '@/interfaces/TransactionInit'
import { MessageInit } from '@/interfaces/MessageInit'
import { TransactionRequest } from '@ethersproject/abstract-provider'
import { Deferrable } from '@ethersproject/properties'
import useAuth from '@/composables/auth'

const defaultProviders = {
Expand All @@ -20,7 +18,6 @@ const availableProviders = ref<BrowserProviders>(getBrowserProviders(ethereum))
export default function useEthers() {
const ethersProviderList = ['MetaMask', 'CoinbaseWallet']

// TODO: Type the parameter
async function requestEthersAccount(provider: EthersProvider) {
if (provider?.request) {
return await provider.request({
Expand All @@ -29,6 +26,13 @@ export default function useEthers() {
}
}

function getEthersBrowserSigner(providerString: ProviderString): ethers.Signer | undefined {
const provider = availableProviders.value[providerString as keyof BrowserProviders]
if (provider) {
return new ethers.providers.Web3Provider(provider as EthersProvider).getSigner()
}
}

async function getEthersAddress (providerString: ProviderString) {
const provider = availableProviders.value[providerString as keyof BrowserProviders]
if (provider) {
Expand All @@ -54,35 +58,35 @@ export default function useEthers() {
}

async function signEthersMessage(messageInit: MessageInit): Promise<string> {
const { providerString, hashedMessage } = messageInit
const { providerString, message } = messageInit
const browserProvider =
availableProviders.value[
providerString as keyof BrowserProviders
]
const web3Provider: ethers.providers.Web3Provider =
new ethers.providers.Web3Provider(browserProvider as EthersProvider)
const signer = web3Provider.getSigner()
const signature = await signer.signMessage(hashedMessage)
const signature = await signer.signMessage(message)

// Todo move this sample code
const { login } = useAuth()
const response = await login({ address: signer._address, message: hashedMessage, signedMessage: signature })
const response = await login({ address: signer._address, message: message.toString(), signedMessage: signature })
console.log('Response', await response.json()) // Currently the response is always false

return signature
}

async function getGasPriceAndLimit(
rpcUrl: string,
unsignedTransaction: Deferrable<TransactionRequest>
unsignedTransaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>
) {
const provider = new ethers.providers.JsonRpcProvider(rpcUrl)
const gasPrice = await provider.getGasPrice()
const gasLimit = await provider.estimateGas(unsignedTransaction as Deferrable<TransactionRequest>)
const gasLimit = await provider.estimateGas(unsignedTransaction as ethers.utils.Deferrable<ethers.providers.TransactionRequest>)
return { gasPrice, gasLimit }
}

return { ethersProviderList, getEthersAddress, sendEthersTransaction, signEthersMessage, getGasPriceAndLimit }
return { ethersProviderList, getEthersBrowserSigner, getEthersAddress, sendEthersTransaction, signEthersMessage, getGasPriceAndLimit }
}

function getBrowserProviders(ethereum: any) {
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/composables/iopay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ export default function useIoPay() {
}

const signIoPayMessage = async (messageInit: MessageInit): Promise<Buffer> => {
const { hashedMessage } = messageInit
return await signer.signMessage(hashedMessage)
const { message } = messageInit
return await signer.signMessage(message)
}

// const stakeIoPay = async () => {
Expand Down
96 changes: 29 additions & 67 deletions apps/web/src/composables/ledger.ts
Original file line number Diff line number Diff line change
@@ -1,104 +1,66 @@
import SpeculosHttpTransport from '@casimir/hw-transport-speculos'
import TransportWebUSB from '@ledgerhq/hw-transport-webusb'
import Eth, { ledgerService } from '@ledgerhq/hw-app-eth'
import EthersLedgerSigner from '@casimir/ethers-ledger-signer'
import { ethers } from 'ethers'
import { TransactionInit } from '@/interfaces/TransactionInit'
import { Deferrable } from '@ethersproject/properties'
import { TransactionRequest } from '@ethersproject/abstract-provider'
import { MessageInit } from '@/interfaces/MessageInit'
import useEthers from './ethers'
import useEnvironment from '@/composables/environment'
import useEthers from '@/composables/ethers'

export default function useLedger() {
const bip32Path = '44\'/60\'/0\'/0/0'
const ledgerPath = '44\'/60\'/0\'/0/0'

async function getLedgerEthSigner() {
const transport = await _getLedgerTransport()
return new Eth(transport)
}
export default function useLedger() {
const { ethereumURL, ledgerType, speculosURL } = useEnvironment()
const { getGasPriceAndLimit } = useEthers()

async function _getLedgerTransport() {
if (import.meta.env.PUBLIC_SPECULOS_PORT) {
return await SpeculosHttpTransport.open(
`http://127.0.0.1:${import.meta.env.PUBLIC_SPECULOS_PORT}`
)
} else {
return await TransportWebUSB.create()
function getEthersLedgerSigner() {
const options = {
provider: new ethers.providers.JsonRpcProvider(ethereumURL),
type: ledgerType,
path: ledgerPath,
baseURL: speculosURL
}
return new EthersLedgerSigner(options)
}

async function getLedgerAddress() {
const ledger = await getLedgerEthSigner()
const { address } = await ledger.getAddress(bip32Path)
await ledger.transport.close()
return address
const signer = getEthersLedgerSigner()
return await signer.getAddress()
}

async function sendLedgerTransaction({ from, to, value }: TransactionInit) {
const rpcUrl = import.meta.env.PUBLIC_ETHEREUM_RPC || 'http://localhost:8545/'
const provider = new ethers.providers.JsonRpcProvider(rpcUrl)
const signer = getEthersLedgerSigner()
const provider = signer.provider as ethers.providers.Provider
const { chainId } = await provider.getNetwork()
const nonce = await provider.getTransactionCount(from)
const unsignedTransaction: ethers.utils.UnsignedTransaction = {
const unsignedTransaction = {
to,
data: '0x00',
nonce,
chainId,
value: ethers.utils.parseUnits(value)
}
const { getGasPriceAndLimit } = useEthers()
const { gasPrice, gasLimit } = await getGasPriceAndLimit(rpcUrl, unsignedTransaction as Deferrable<TransactionRequest>)
} as ethers.UnsignedTransaction
const { gasPrice, gasLimit } = await getGasPriceAndLimit(ethereumURL, unsignedTransaction as ethers.utils.Deferrable<ethers.providers.TransactionRequest>)
unsignedTransaction.gasPrice = gasPrice
unsignedTransaction.gasLimit = gasLimit

// Todo check before click (user can +/- gas limit accordingly)
const balance = await provider.getBalance(from)
const required = gasPrice.mul(gasLimit).add(ethers.utils.parseEther(value))
console.log('Balance', ethers.utils.formatEther(balance))
console.log('Required', ethers.utils.formatEther(required))

const ledger = await getLedgerEthSigner()
const rawUnsignedTransaction = ethers.utils
.serializeTransaction(unsignedTransaction)
.substring(2)
const resolution = await ledgerService.resolveTransaction(
rawUnsignedTransaction,
{},
{}
)
const { v, r, s } = await ledger.signTransaction(
bip32Path,
rawUnsignedTransaction,
resolution
)
const signature = {
v: parseInt(v),
r: '0x' + r,
s: '0x' + s,
from
}
const signedTransaction = ethers.utils.serializeTransaction(
unsignedTransaction,
signature
)
await ledger.transport.close()
return await provider.sendTransaction(signedTransaction)
return await signer.sendTransaction(unsignedTransaction as ethers.utils.Deferrable<ethers.providers.TransactionRequest>)
}

async function signLedgerMessage(messageInit: MessageInit): Promise<string> {
const { hashedMessage } = messageInit
const _eth = await getLedgerEthSigner()
const signature = await _eth.signPersonalMessage(
bip32Path,
Buffer.from(hashedMessage).toString('hex')
)
const signedHash =
'0x' + signature.r + signature.s + signature.v.toString(16)
return signedHash
const { message } = messageInit
const signer = getEthersLedgerSigner()
return await signer.signMessage(message)
}

return {
bip32Path,
ledgerPath,
getLedgerAddress,
getLedgerEthSigner,
getEthersLedgerSigner,
signLedgerMessage,
sendLedgerTransaction,
}
Expand Down
Loading

0 comments on commit 3cd0add

Please sign in to comment.