Skip to content

Commit

Permalink
feat: add filforwarder transaction insights
Browse files Browse the repository at this point in the history
  • Loading branch information
hugomrdias committed Dec 30, 2024
1 parent 64c5f57 commit abf7d5a
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 139 deletions.
6 changes: 5 additions & 1 deletion packages/snap/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/filecoin-project/filsnap.git"
},
"source": {
"shasum": "wf1YV/Pkw0eO4py8+gkxF+6l1CtEzMW9BjJUS1K137s=",
"shasum": "zM6X5ZlF8x7w0kFoPr6nY85isY21RDnHvjjPVNBy5so=",
"location": {
"npm": {
"filePath": "dist/snap.js",
Expand All @@ -25,6 +25,9 @@
"endowment:page-home": {},
"endowment:lifecycle-hooks": {},
"endowment:network-access": {},
"endowment:transaction-insight": {
"allowTransactionOrigin": true
},
"endowment:rpc": {
"dapps": true
},
Expand All @@ -39,5 +42,6 @@
],
"snap_manageState": {}
},
"platformVersion": "6.14.0",
"manifestVersion": "0.1"
}
35 changes: 21 additions & 14 deletions packages/snap/src/components/error.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
import {
Box,
Heading,
Row,
type SnapComponent,
Text,
} from '@metamask/snaps-sdk/jsx'

import { Box, Image, type SnapComponent, Text } from '@metamask/snaps-sdk/jsx'
import iconError from '../svg/error.svg'
type ErrorBoxProps = {
error: string
name: string
message?: string
}
export const ErrorBox: SnapComponent<ErrorBoxProps> = ({ error }) => {
export const ErrorBox: SnapComponent<ErrorBoxProps> = ({ name, message }) => {
if (message != null) {
return (
<Box>
<Box direction="horizontal">
<Image src={iconError} />
<Text color="error">{name}</Text>
</Box>
<Text color="muted">{message}</Text>
</Box>
)
}

return (
<Box>
<Heading>Error</Heading>
<Row label="Message:" variant="critical">
<Text>{error}</Text>
</Row>
<Box direction="horizontal">
<Image src={iconError} />
<Text color="error">{name}</Text>
</Box>
</Box>
)
}
53 changes: 53 additions & 0 deletions packages/snap/src/components/insights.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
Bold,
Box,
Copyable,
Divider,
Heading,
Icon,
Link,
type SnapComponent,
Text,
} from '@metamask/snaps-sdk/jsx'
import type { SnapConfig } from '../types'
import { formatFIL } from '../utils'

type ErrorBoxProps = {
config: SnapConfig
address: string
amount: string
}
export const Insights: SnapComponent<ErrorBoxProps> = ({
amount,
config,
address,
}) => {
return (
<Box>
<Heading>FilForwarder Transaction</Heading>
<Text>
The FilForwarder smart contract enables FEVM users to send their FIL
safely and securely to other addresses in the Filecoin ecosystem. Review
the <Link href="https://github.com/FilOzone/FilForwarder">source</Link>{' '}
and onchain{' '}
<Link href="https://beryx.io/fil/mainnet/address/f410ffm7pnedefg2ybn5sbag6lsujhpbifqrfspbcqna">
deployment
</Link>{' '}
for more information.
</Text>
<Divider />
<Box direction="horizontal">
<Icon name="user" size="md" />
<Text>Recipient</Text>
</Box>
<Copyable value={address} />
<Box direction="horizontal">
<Text>
<Bold></Bold>
</Text>
<Text>Amount</Text>
</Box>
<Text color="muted">{formatFIL(amount, config)}</Text>
</Box>
)
}
8 changes: 3 additions & 5 deletions packages/snap/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export type {
} from './types'

// Disable transaction insight for now
// export { onTransaction } from './transaction-insight'
export { onTransaction } from './transaction-insight'

export const onRpcRequest: OnRpcRequestHandler = async ({
origin,
Expand Down Expand Up @@ -164,7 +164,7 @@ export const onHomePage: OnHomePageHandler = async () => {

if (config === undefined) {
return {
content: <ErrorBox error={'Error no config!'} />,
content: <ErrorBox name={'Error no config!'} />,
}
}

Expand All @@ -183,9 +183,7 @@ export const onHomePage: OnHomePageHandler = async () => {
const balance = await rpc.balance(account.address.toString())
if (balance.error != null) {
return {
content: (
<ErrorBox error={`Error calling RPC ${balance.error.message}`} />
),
content: <ErrorBox name={`Error calling RPC ${balance.error.message}`} />,
}
}
return {
Expand Down
1 change: 1 addition & 0 deletions packages/snap/src/svg/error.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
119 changes: 0 additions & 119 deletions packages/snap/src/transaction-insight.ts

This file was deleted.

115 changes: 115 additions & 0 deletions packages/snap/src/transaction-insight.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import type {
OnTransactionHandler,
OnTransactionResponse,
} from '@metamask/snaps-sdk'
import * as Address from 'iso-filecoin/address'
import { Token } from 'iso-filecoin/token'
import { type Hex, fromHex } from 'viem'
import { decodeFunctionData } from 'viem'
import { ErrorBox } from './components/error'
import { Insights } from './components/insights'
import { filForwarderMetadata } from './filforwarder'
import { State } from './state'

/**
* Chain matches
*
* @param chainId - The chain ID
*/
function chainMatches(chainId: string): boolean {
return Object.values(filForwarderMetadata.chainIds).includes(chainId)
}

/**
* Contract address matches
*
* @param transactionTo - The transaction to address
*/
function contractAddressMatches(transactionTo: string | undefined): boolean {
return (
transactionTo?.toLowerCase() ===
filForwarderMetadata.contractAddress.toLowerCase()
)
}

export const onTransaction: OnTransactionHandler = async ({
transaction,
chainId,
transactionOrigin,
}): Promise<OnTransactionResponse | null> => {
if (!transactionOrigin) {
return {
content: (
<ErrorBox
name={'Internal error'}
message={'Missing transaction origin'}
/>
),
}
}
const state = new State(snap)
const config = await state.get(transactionOrigin)
if (!config) {
return {
content: <ErrorBox name={'Internal error'} message={'Missing config'} />,
}
}

// Don't show any insights if the transaction is not a FIL transfer.
if (
!chainMatches(chainId) ||
!contractAddressMatches(transaction.to as string | undefined)
) {
return null
}

try {
if (!transaction.value) {
return {
content: (
<ErrorBox name={'Transfer amount is missing from the transaction.'} />
),
}
}

const callData = decodeFunctionData({
abi: filForwarderMetadata.abi,
data: transaction.data as Hex,
})

if (callData.functionName !== 'forward') {
return {
content: <ErrorBox name={'Transaction tries to call wrong method.'} />,
}
}
if (callData.args === undefined || callData.args.length !== 1) {
return {
content: <ErrorBox name={'Missing recipient in transaction.'} />,
}
}

const isMainNet = chainId === filForwarderMetadata.chainIds.filecoinMainnet
const transferAmount = Token.fromFIL(
fromHex(transaction.value as Hex, 'bigint')
)
const recipient = Address.fromContractDestination(
callData.args[0] as Hex,
isMainNet ? 'mainnet' : 'testnet'
)
return {
content: (
<Insights
config={config}
amount={transferAmount.toFIL().toString()}
address={recipient.toString()}
/>
),
}
} catch (error) {
const err = error as Error
console.error(error)
return {
content: <ErrorBox name={err.name} message={err.message} />,
}
}
}

0 comments on commit abf7d5a

Please sign in to comment.