Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: wallet api - part 1/2 #333

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7a5a3f6
feat: move station rpc into hook
jopedroliveira Nov 29, 2022
13d9046
feat: backend interface and wire up for wallet
jopedroliveira Nov 29, 2022
848815d
wip: ensure compatibility
jopedroliveira Nov 29, 2022
2a08e23
eslint
jopedroliveira Nov 29, 2022
6c7787b
Update main/ipc.js
jopedroliveira Dec 16, 2022
384fdce
Update main/ipc.js
jopedroliveira Dec 16, 2022
2684c87
Update main/preload.js
jopedroliveira Dec 16, 2022
3d17c23
Update main/preload.js
jopedroliveira Dec 16, 2022
e6ae80c
Update main/station-config.js
jopedroliveira Dec 16, 2022
9694821
Update renderer/src/lib/station-config.tsx
jopedroliveira Dec 16, 2022
ed75855
feat: backend interface and wire up for wallet
jopedroliveira Nov 29, 2022
735a5d0
wip: ensure compatibility
jopedroliveira Nov 29, 2022
762e1b4
eslint
jopedroliveira Nov 29, 2022
8f848a3
Update main/ipc.js
jopedroliveira Dec 16, 2022
d26055c
Update main/ipc.js
jopedroliveira Dec 16, 2022
f60c57e
Update main/preload.js
jopedroliveira Dec 16, 2022
7a6a5af
Update main/preload.js
jopedroliveira Dec 16, 2022
9aa20a1
Update main/station-config.js
jopedroliveira Dec 16, 2022
56f9e79
Update renderer/src/lib/station-config.tsx
jopedroliveira Dec 16, 2022
5fb5bed
Merge branch 'feat/wallet-api' of github.com:jopedroliveira/filecoin-…
jopedroliveira Dec 16, 2022
35e7d26
fixup: remove address check on onboarding page
jopedroliveira Dec 16, 2022
034e18f
refactor: staturn fe component startup
jopedroliveira Dec 16, 2022
dd789ef
fixup: lint
jopedroliveira Dec 16, 2022
073ec79
Update renderer/src/typings.d.ts
jopedroliveira Dec 16, 2022
51f8756
Merge branch 'feat/wallet' into feat/wallet-api
juliangruber Jan 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions main/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

const { app, dialog } = require('electron')
const { app, dialog, shell } = require('electron')
const log = require('electron-log')
const path = require('node:path')

Expand Down Expand Up @@ -108,7 +108,8 @@ const ctx = {
confirmChangeWalletAddress: () => { throw new Error('never get here') },
restartToUpdate: () => { throw new Error('never get here') },
openReleaseNotes: () => { throw new Error('never get here') },
getUpdaterStatus: () => { throw new Error('never get here') }
getUpdaterStatus: () => { throw new Error('never get here') },
browseTransactionTracker: (/** @type {string} */ transactionHash) => { shell.openExternal(`https://explorer.glif.io/tx/${transactionHash}`) }
}

app.on('before-quit', () => {
Expand Down
10 changes: 8 additions & 2 deletions main/ipc.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,15 @@ function setupIpcMain (/** @type {Context} */ ctx) {
ipcMain.handle('saturn:getFilAddress', saturnNode.getFilAddress)
ipcMain.handle('saturn:setFilAddress', (_event, address) => saturnNode.setFilAddress(address))
// Station-wide config
ipcMain.handle('station:getFilAddress', saturnNode.getFilAddress)
ipcMain.handle('station:setFilAddress', (_event, address) => saturnNode.setFilAddress(address))
ipcMain.handle('station:getOnboardingCompleted', stationConfig.getOnboardingCompleted)
ipcMain.handle('station:setOnboardingCompleted', (_event) => stationConfig.setOnboardingCompleted())
// Wallet-wide config
ipcMain.handle('station:getStationWalletAddress', stationConfig.getStationWalletAddress)
ipcMain.handle('station:getDestinationWalletAddress', stationConfig.getDestinationWalletAddress)
ipcMain.handle('station:setDestinationWalletAddress', (_event, address) => stationConfig.setDestinationWalletAddress(address))
ipcMain.handle('station:getStationWalletBalance', stationConfig.getStationWalletBalance)
ipcMain.handle('station:getStationWalletTransactionsHistory', stationConfig.getStationWalletTransactionsHistory)
ipcMain.handle('station:trasnferAllFundsToDestinationWallet', (_event, _args) => stationConfig.trasnferAllFundsToDestinationWallet())

ipcMain.handle('station:getAllActivities', (_event, _args) => ctx.getAllActivities())
ipcMain.handle('station:getTotalJobsCompleted', (_event, _args) => ctx.getTotalJobsCompleted())
Expand All @@ -39,6 +44,7 @@ function setupIpcMain (/** @type {Context} */ ctx) {
ipcMain.handle('station:restartToUpdate', (_event, _args) => ctx.restartToUpdate())
ipcMain.handle('station:openReleaseNotes', (_event) => ctx.openReleaseNotes())
ipcMain.handle('station:getUpdaterStatus', (_events, _args) => ctx.getUpdaterStatus())
ipcMain.handle('station:browseTransactionTracker', (_events, transactoinHash) => ctx.browseTransactionTracker(transactoinHash))
}

module.exports = {
Expand Down
28 changes: 23 additions & 5 deletions main/preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

/** @typedef {import('electron').IpcRendererEvent} IpcRendererEvent */
/** @typedef {import('./typings').Activity} Activity */
/** @typedef {import('./typings').FILTransaction} TransactionMessage */

const { contextBridge, ipcRenderer } = require('electron')

Expand All @@ -22,14 +23,19 @@ contextBridge.exposeInMainWorld('electron', {
isReady: () => ipcRenderer.invoke('saturn:isReady'),
getLog: () => ipcRenderer.invoke('saturn:getLog'),
getWebUrl: () => ipcRenderer.invoke('saturn:getWebUrl'),
getFilAddress: () => ipcRenderer.invoke('saturn:getFilAddress'),
setFilAddress: (/** @type {string | undefined} */ address) => ipcRenderer.invoke('saturn:setFilAddress', address)
getFilAddress: () => ipcRenderer.invoke('saturn:getFilAddress'), // soon to be removed
setFilAddress: (/** @type {string | undefined} */ address) => ipcRenderer.invoke('saturn:setFilAddress', address) // soon to be removed
},
stationConfig: {
getFilAddress: () => ipcRenderer.invoke('station:getFilAddress'),
setFilAddress: (/** @type {string | undefined} */ address) => ipcRenderer.invoke('station:setFilAddress', address),
getOnboardingCompleted: () => ipcRenderer.invoke('station:getOnboardingCompleted'),
setOnboardingCompleted: () => ipcRenderer.invoke('station:setOnboardingCompleted')
setOnboardingCompleted: () => ipcRenderer.invoke('station:setOnboardingCompleted'),
getStationWalletAddress: () => ipcRenderer.invoke('station:getStationWalletAddress'),
getDestinationWalletAddress: () => ipcRenderer.invoke('station:getDestinationWalletAddress'),
setDestinationWalletAddress: (/** @type {string | undefined} */ address) => ipcRenderer.invoke('station:setDestinationWalletAddress', address),
getStationWalletBalance: () => ipcRenderer.invoke('station:getStationWalletBalance'),
getStationWalletTransactionsHistory: () => ipcRenderer.invoke('station:getStationWalletTransactionsHistory'),
trasnferAllFundsToDestinationWallet: () => ipcRenderer.invoke('station:trasnferAllFundsToDestinationWallet'),
browseTransactionTracker: (/** @type {string } */ transactoinHash) => ipcRenderer.invoke('station:browseTransactionTracker', transactoinHash)
},
stationEvents: {
onActivityLogged: (/** @type {(value: Activity) => void} */ callback) => {
Expand All @@ -54,6 +60,18 @@ contextBridge.exposeInMainWorld('electron', {
const listener = () => callback()
ipcRenderer.on('station:update-available', listener)
return () => ipcRenderer.removeListener('station:update-available', listener)
},
onBalanceUpdate: (/** @type {(value: number) => void} */ callback) => {
/** @type {(event: IpcRendererEvent, ...args: any[]) => void} */
const listener = (_event, balance) => callback(balance)
ipcRenderer.on('station:wallet-balance-update', listener)
return () => ipcRenderer.removeListener('station:wallet-balance-update', listener)
},
onTransactionUpdate: (/** @type {(value: TransactionMessage) => void} */ callback) => {
/** @type {(event: IpcRendererEvent, ...args: any[]) => void} */
const listener = (_event, transactions) => callback(transactions)
ipcRenderer.on('station:transaction-update', listener)
return () => ipcRenderer.removeListener('station:transaction-update', listener)
}
},
dialogs: {
Expand Down
59 changes: 56 additions & 3 deletions main/station-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ const ConfigKeys = {
OnboardingCompleted: 'station.OnboardingCompleted',
TrayOperationExplained: 'station.TrayOperationExplained',
StationID: 'station.StationID',
FilAddress: 'station.FilAddress'
FilAddress: 'station.FilAddress',
DestinationFilAddress: 'station.FilAddress' // todo - replace by 'station.DestinationFilAddress'
}

/** @typedef {import('./typings').FILTransaction} TransactionMessage */

// Use this to test migrations
// https://github.com/sindresorhus/electron-store/issues/205
// require('electron').app.setVersion('9999.9.9')
Expand All @@ -35,6 +38,7 @@ console.log('Loading Station configuration from', configStore.path)
let OnboardingCompleted = /** @type {boolean} */ (configStore.get(ConfigKeys.OnboardingCompleted, false))
let TrayOperationExplained = /** @type {boolean} */ (configStore.get(ConfigKeys.TrayOperationExplained, false))
let FilAddress = /** @type {string | undefined} */ (configStore.get(ConfigKeys.FilAddress))
let DestinationFilAddress = /** @type {string | undefined} */ (configStore.get(ConfigKeys.DestinationFilAddress))
const StationID = /** @type {string} */ (configStore.get(ConfigKeys.StationID, randomUUID()))

/**
Expand Down Expand Up @@ -85,16 +89,65 @@ function setFilAddress (address) {
/**
* @returns {string}
*/
function getStationID () {
function getStationID() {
return StationID
}

/**
* @returns {string}
*/
function getStationWalletAddress() {
return FilAddress || '' // needs refactor
}

/**
* @returns {string | undefined}
*/
function getDestinationWalletAddress() {
return DestinationFilAddress
}

/**
* @param {string | undefined} address
*/
function setDestinationWalletAddress(address) {
DestinationFilAddress = address
configStore.set(ConfigKeys.DestinationFilAddress, DestinationFilAddress)
}

/**
* @returns {number}
*/
function getStationWalletBalance() {
return 0 // todo - backend logic
}

/**
* @returns { TransactionMessage[] }
*/
function getStationWalletTransactionsHistory() {
return [] // todo - backend logic
}

/**
* @returns void
*/
function trasnferAllFundsToDestinationWallet() {
return {} // todo - backend logic
}

module.exports = {
getOnboardingCompleted,
setOnboardingCompleted,
getTrayOperationExplained,
setTrayOperationExplained,
getFilAddress,
setFilAddress,
getStationID
getStationID,
getStationWalletAddress,
getDestinationWalletAddress,
setDestinationWalletAddress,
getStationWalletBalance,
getStationWalletTransactionsHistory,
trasnferAllFundsToDestinationWallet
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
trasnferAllFundsToDestinationWallet
transferAllFundsToDestinationWallet

}
13 changes: 12 additions & 1 deletion main/typings.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export type ActivitySource = 'Station' | 'Saturn';
export type ActivityType = 'info' | 'error';
export type TransactionStatus = 'sent' | 'processing' | 'failed'

export interface Activity {
id: string;
Expand All @@ -9,6 +10,15 @@ export interface Activity {
message: string;
}

export type FILTransaction = {
hash: string
timestamp: number
status: TransactionStatus
outgoing: boolean
amount: string
address: string
}

export type RecordActivityArgs = Omit<Activity, 'id' | 'timestamp'>;

export type ModuleJobStatsMap = Record<string, number>;
Expand All @@ -28,5 +38,6 @@ export interface Context {

openReleaseNotes: () => void,
restartToUpdate: () => void,
getUpdaterStatus: () => {updateAvailable: boolean}
getUpdaterStatus: () => {updateAvailable: boolean},
browseTransactionTracker: (transactionHash: string) => void
}
4 changes: 2 additions & 2 deletions renderer/src/components/Saturn.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useEffect } from 'react'
import { getFilAddress, startSaturnNode } from '../lib/station-config'
import { getDestinationWalletAddress, startSaturnNode } from '../lib/station-config'

const Saturn = () => {
useEffect(() => {
(async () => {
if (await getFilAddress()) {
if (await getDestinationWalletAddress()) {
startSaturnNode()
}
})()
Expand Down
54 changes: 54 additions & 0 deletions renderer/src/hooks/StationActivity.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useState, useEffect } from 'react'
import { getTotalJobsCompleted, getAllActivities, getTotalEarnings } from '../lib/station-config'
import { ActivityEventMessage } from '../typings'

interface StationActivity {
totalJobs: number,
totalEarnings: number,
activities: ActivityEventMessage[] | []
}
const useStationActivity = (): StationActivity => {
const [totalJobs, setTotalJobs] = useState<number>(0)
const [activities, setActivities] = useState<ActivityEventMessage[]>([])
const [totalEarnings, setTotalEarnigs] = useState<number>(0)

useEffect(() => {
const loadStoredInfo = async () => setTotalJobs(await getTotalJobsCompleted())
loadStoredInfo()
}, [])

useEffect(() => {
const loadStoredInfo = async () => setActivities(await getAllActivities())
loadStoredInfo()
}, [])

useEffect(() => {
const loadStoredInfo = async () => setTotalEarnigs(await getTotalEarnings())
loadStoredInfo()
}, [])

useEffect(() => {
const unsubscribeOnJobProcessed = window.electron.stationEvents.onJobProcessed(setTotalJobs)
return () => {
unsubscribeOnJobProcessed()
}
}, [])

useEffect(() => {
const unsubscribeOnActivityLogged = window.electron.stationEvents.onActivityLogged(setActivities)
return () => {
unsubscribeOnActivityLogged()
}
}, [])

useEffect(() => {
const unsubscribeOnEarningsChanged = window.electron.stationEvents.onEarningsChanged(setTotalEarnigs)
return () => {
unsubscribeOnEarningsChanged()
}
}, [])

return { totalJobs, totalEarnings, activities }
}

export default useStationActivity
98 changes: 98 additions & 0 deletions renderer/src/hooks/StationWallet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { useState, useEffect } from 'react'
import {
getDestinationWalletAddress,
setDestinationWalletAddress,
getStationWalletAddress,
getStationWalletBalance,
getStationWalletTransactionsHistory
} from '../lib/station-config'
import { FILTransaction } from '../typings'

interface Wallet {
stationAddress: string,
destinationFilAddress: string | undefined,
walletBalance: number,
walletTransactions: FILTransaction[] | [],
editDestinationAddress: (address: string|undefined) => void,
currentTransaction: FILTransaction | undefined,
dismissCurrentTransaction: () => void
}

const useWallet = (): Wallet => {
const [stationAddress, setStationAddress] = useState<string>('')
const [destinationFilAddress, setDestinationFilAddress] = useState<string | undefined>()
const [walletBalance, setWalletBalance] = useState<number>(0)
const [walletTransactions, setWalletTransactions] = useState<FILTransaction[]>([])
const [currentTransaction, setCurrentTransaction] = useState<FILTransaction | undefined>()

const editDestinationAddress = async (address: string | undefined) => {
await setDestinationWalletAddress(address)
setDestinationFilAddress(address)
}

const dismissCurrentTransaction = () => {
if (currentTransaction && currentTransaction.status !== 'processing') {
setWalletTransactions([currentTransaction, ...walletTransactions])
setCurrentTransaction(undefined)
}
}

useEffect(() => {
const loadStoredInfo = async () => {
setDestinationFilAddress(await getDestinationWalletAddress())
}
loadStoredInfo()
}, [destinationFilAddress])

useEffect(() => {
const loadStoredInfo = async () => {
setStationAddress(await getStationWalletAddress())
}
loadStoredInfo()
}, [stationAddress])

useEffect(() => {
const loadStoredInfo = async () => {
setWalletBalance(await getStationWalletBalance())
}
loadStoredInfo()
}, [])

useEffect(() => {
const loadStoredInfo = async () => {
setWalletTransactions(await getStationWalletTransactionsHistory())
}
loadStoredInfo()
}, [])

useEffect(() => {
const updateWalletTransactionsArray = (transactions: FILTransaction[]) => {
const newCurrentTransaction = transactions[0]
if (newCurrentTransaction.status === 'processing' || (currentTransaction && +currentTransaction.timestamp === +newCurrentTransaction.timestamp)) {
setCurrentTransaction(newCurrentTransaction)
if (newCurrentTransaction.status !== 'processing') { setTimeout(() => { setWalletTransactions(transactions); setCurrentTransaction(undefined) }, 6000) }

const transactionsExceptLatest = transactions.filter((t) => { return t !== newCurrentTransaction })
setWalletTransactions(transactionsExceptLatest)
} else {
setWalletTransactions(transactions)
}
}

const unsubscribeOnTransactionUpdate = window.electron.stationEvents.onTransactionUpdate(updateWalletTransactionsArray)
return () => {
unsubscribeOnTransactionUpdate()
}
}, [currentTransaction])

useEffect(() => {
const unsubscribeOnBalanceUpdate = window.electron.stationEvents.onBalanceUpdate(setWalletBalance)
return () => {
unsubscribeOnBalanceUpdate()
}
}, [walletBalance])

return { stationAddress, destinationFilAddress, walletBalance, walletTransactions, editDestinationAddress, currentTransaction, dismissCurrentTransaction }
}

export default useWallet
Loading