Skip to content

Commit

Permalink
Integrate Filfox into scanning routine
Browse files Browse the repository at this point in the history
  • Loading branch information
samholmes committed Sep 7, 2023
1 parent 68d858a commit bb024b9
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 2 deletions.
83 changes: 81 additions & 2 deletions src/filecoin/FilecoinEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
FilecoinWalletOtherData,
SafeFilecoinWalletInfo
} from './filecoinTypes'
import { Filfox, FilfoxMessage } from './Filfox'
import { Filscan, FilscanMessage } from './Filscan'
import { RpcExtra } from './RpcExtra'

Expand All @@ -53,6 +54,7 @@ export class FilecoinEngine extends CurrencyEngine<

// Backends:
filRpc: RPC
filfoxApi: Filfox
filscanApi: Filscan
rpcExtra: RpcExtra

Expand All @@ -70,6 +72,7 @@ export class FilecoinEngine extends CurrencyEngine<
url: env.networkInfo.rpcNode.url,
token: env.currencyInfo.currencyCode
})
this.filfoxApi = new Filfox(env.networkInfo.filfoxUrl, env.io.fetchCors)
this.filscanApi = new Filscan(env.networkInfo.filscanUrl, env.io.fetchCors)
this.rpcExtra = new RpcExtra(env.networkInfo.rpcNode.url, env.io.fetchCors)

Expand Down Expand Up @@ -333,14 +336,58 @@ export class FilecoinEngine extends CurrencyEngine<
}

const scanners = [
this.scanTransactionsFromFilscan(addressString, handleScan)
this.scanTransactionsFromFilscan(addressString, handleScan),
this.scanTransactionsFromFilfox(addressString, handleScan)
]

await Promise.all(scanners)

handleScanProgress(1)
}

async scanTransactionsFromFilfox(
address: string,
onScan: (event: { tx: EdgeTransaction; progress: number }) => void
): Promise<void> {
const messagesPerPage = 20
let index = 0
let messagesChecked = 0
let messageCount = -1
do {
const messagesResponse = await this.filfoxApi.getAccountMessages(
address,
index++,
messagesPerPage
)

// Only update the message count on the first query because mutating this
// in-between pagination may cause infinite loops.
messageCount =
messageCount === -1 ? messagesResponse.totalCount : messageCount

const messages = messagesResponse.messages
for (const message of messages) {
const txid = message.cid
const idx = this.findTransaction(this.currencyInfo.currencyCode, txid)

if (idx >= 0) {
// Exit early because we reached transaction history from previous
// check
return
}

// Process message into a transaction
const tx = this.filfoxMessageToEdgeTransaction(message)

// Calculate the progress
const progress =
messageCount === 0 ? 1 : ++messagesChecked / messageCount

onScan({ tx: tx, progress })
}
} while (messagesChecked < messageCount)
}

async scanTransactionsFromFilscan(
address: string,
onScan: (event: { tx: EdgeTransaction; progress: number }) => void
Expand Down Expand Up @@ -379,11 +426,43 @@ export class FilecoinEngine extends CurrencyEngine<
const progress =
messageCount === 0 ? 1 : ++messagesChecked / messageCount

onScan({ tx: tx, progress })
onScan({ tx, progress })
}
} while (messagesChecked < messageCount)
}

filfoxMessageToEdgeTransaction = (
message: FilfoxMessage
): EdgeTransaction => {
const addressString = this.address.toString()
let netNativeAmount = message.value
const ourReceiveAddresses = []

const networkFee = '0' // TODO: calculate transaction fee from onchain gas fields
if (message.to !== addressString) {
// check if tx is a spend
netNativeAmount = `-${add(netNativeAmount, networkFee)}`
} else {
ourReceiveAddresses.push(addressString)
}

const edgeTransaction: EdgeTransaction = {
txid: message.cid,
date: message.timestamp,
currencyCode: this.currencyInfo.currencyCode,
blockHeight: message.height,
nativeAmount: netNativeAmount,
isSend: netNativeAmount.startsWith('-'),
networkFee,
ourReceiveAddresses, // blank if you sent money otherwise array of addresses that are yours in this transaction
signedTx: '',
otherParams: {},
walletId: this.walletId
}

return edgeTransaction
}

filscanMessageToEdgeTransaction(message: FilscanMessage): EdgeTransaction {
const addressString = this.address.toString()
let netNativeAmount = message.value
Expand Down
101 changes: 101 additions & 0 deletions src/filecoin/Filfox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import {
asArray,
asEither,
asJSON,
asNumber,
asObject,
asString,
Cleaner
} from 'cleaners'
import { EdgeFetchFunction } from 'edge-core-js/types'

// -----------------------------------------------------------------------------
// Types
// -----------------------------------------------------------------------------

//
// Response Templates
//

export type FilfoxError = ReturnType<typeof asFilfoxError>
export const asFilfoxError = asObject({
statusCode: asNumber,
message: asString,
error: asString
})

export type FilfoxEnvelope<Result> = Result | FilfoxError
export const asFilfoxEnvelope = <Result>(
asResult: Cleaner<Result>
): Cleaner<FilfoxEnvelope<Result>> => asJSON(asEither(asFilfoxError, asResult))

//
// Nominal Types
//

export type FilfoxMessage = ReturnType<typeof asFilfoxMessage>
export const asFilfoxMessage = asObject({
cid: asString,
from: asString,
height: asNumber,
method: asString,
nonce: asNumber,
reciept: asObject({
exitCode: asNumber
}),
timestamp: asNumber,
to: asString,
value: asString
})

//
// Messages
//

export type FilfoxMessagesResult = ReturnType<typeof asFilfoxMessagesResult>
export const asFilfoxMessagesResult = asObject({
messages: asArray(asFilfoxMessage),
totalCount: asNumber
})

// -----------------------------------------------------------------------------
// Implementation
// -----------------------------------------------------------------------------

export class Filfox {
baseUrl: string
fetch: EdgeFetchFunction

constructor(baseUrl: string, fetchFn: EdgeFetchFunction) {
this.baseUrl = baseUrl
this.fetch = fetchFn
}

async getAccountMessages(
address: string,
page: number,
pageSize: number = 20
): Promise<FilfoxMessagesResult> {
const url = new URL(`${this.baseUrl}/address/${address}/messages`)
const searchParams = new URLSearchParams({
page: page.toString(),
pageSize: pageSize.toString()
})
url.search = searchParams.toString()
const response = await this.fetch(url.toString(), {
method: 'GET',
headers: {
'content-type': 'application/json'
}
})
const responseText = await response.text()
const responseBody = asFilfoxEnvelope(asFilfoxMessagesResult)(responseText)

if ('error' in responseBody)
throw new Error(
`Error response code ${responseBody.statusCode}: ${responseBody.message} ${responseBody.error}`
)

return responseBody
}
}
1 change: 1 addition & 0 deletions src/filecoin/filecoinInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { FilecoinTools } from './FilecoinTools'
import type { FilecoinNetworkInfo } from './filecoinTypes'

const networkInfo: FilecoinNetworkInfo = {
filfoxUrl: 'https://filfox.info/api/v1',
filscanUrl: 'https://api-v2.filscan.io/api/v1',
hdPathCoinType: 461,
rpcNode: {
Expand Down
1 change: 1 addition & 0 deletions src/filecoin/filecoinTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ declare enum Network {
Butterfly = 'butterfly'
}
export interface FilecoinNetworkInfo {
filfoxUrl: string
filscanUrl: string
hdPathCoinType: number
rpcNode: {
Expand Down

0 comments on commit bb024b9

Please sign in to comment.