Skip to content

Commit

Permalink
feat: show more info about contract calls (#1749)
Browse files Browse the repository at this point in the history
- Closes #1625

---

| 📷 Bako | SwayLend | Bridge |
| --- | --- | --- |
| <img width="366" alt="Screenshot 2025-01-01 at 21 48 09"
src="https://github.com/user-attachments/assets/279fba87-e66a-4228-84ee-a740f1f3bd6d"
/> | <img width="375" alt="Screenshot 2025-01-01 at 21 48 43"
src="https://github.com/user-attachments/assets/a33b1773-543d-4c87-91b8-b55c70fa05eb"
/> | <img width="338" alt="Screenshot 2025-01-01 at 21 52 38"
src="https://github.com/user-attachments/assets/1ccc5f2d-2431-4c41-8bcd-58a682a8feda"
/> |
  • Loading branch information
helciofranco authored Jan 2, 2025
1 parent d41f8ec commit 9cb81db
Show file tree
Hide file tree
Showing 22 changed files with 423 additions and 192 deletions.
6 changes: 6 additions & 0 deletions .changeset/popular-mirrors-roll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@fuel-wallet/types": minor
"fuels-wallet": minor
---

Add contract logos and names to the transaction screen for better UX.
2 changes: 1 addition & 1 deletion packages/app/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ VITE_CRX_NAME="Fuel Wallet Development"
VITE_CRX_VERSION_API="https://fuellabs.github.io/fuels-wallet/latest.json"
VITE_FUEL_PROVIDER_URL=http://localhost:4000/v1/graphql
VITE_FUEL_FAUCET_URL=http://localhost:4040
VITE_EXPLORER_URL=https://app.fuel.network/
VITE_ECOSYSTEM_PROJECTS_URL=https://fuellabs.github.io/fuel-ecosystem/projects.json
VITE_MNEMONIC_WORDS=12
GENESIS_SECRET=0xa449b1ffee0e2205fa924c6740cc48b3b473aa28587df6dab12abc245d1f5298
VITE_AUTO_LOCK_IN_MINUTES=1
Expand Down
2 changes: 1 addition & 1 deletion packages/app/.env.production
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ VITE_CRX_VERSION_API="https://fuellabs.github.io/fuels-wallet/latest.json"
VITE_CRX_RELEASE=true
VITE_FUEL_PROVIDER_URL=https://testnet.fuel.network/v1/graphql
VITE_FUEL_FAUCET_URL=https://faucet-testnet.fuel.network/
VITE_EXPLORER_URL=https://app.fuel.network/
VITE_ECOSYSTEM_PROJECTS_URL=https://fuellabs.github.io/fuel-ecosystem/projects.json
VITE_MNEMONIC_WORDS=12
VITE_AUTO_LOCK_IN_MINUTES=2880
VITE_SENTRY_DSN=https://f9a5b923067f4f789fb40ed7eccd6486@o4505280621707264.ingest.sentry.io/4505280648577024
1 change: 1 addition & 0 deletions packages/app/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ declare namespace NodeJS {
VITE_DATABASE_VERSION: string;
readonly VITE_FUEL_PROVIDER_URL: string;
readonly VITE_FUEL_FAUCET_URL: string;
readonly VITE_ECOSYSTEM_PROJECTS_URL: string;
readonly VITE_MNEMONIC_WORDS: string;
readonly VITE_CRX_NAME: string;
readonly VITE_CRX_VERSION_API: string;
Expand Down
2 changes: 0 additions & 2 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
"@sentry/react": "8.33.1",
"@storybook/addon-viewport": "7.4.6",
"@storybook/jest": "0.2.3",
"@tanstack/react-query": "5.28.4",
"@xstate/react": "3.2.2",
"compare-versions": "6.1.0",
"cross-fetch": "4.0.0",
Expand Down Expand Up @@ -84,7 +83,6 @@
"@storybook/react-webpack5": "7.4.6",
"@storybook/testing-library": "0.2.2",
"@storybook/theming": "7.4.6",
"@tanstack/react-query-devtools": "5.28.4",
"@testing-library/react": "14.0.0",
"@types/chrome": "0.0.246",
"@types/lodash.debounce": "4.0.7",
Expand Down
4 changes: 2 additions & 2 deletions packages/app/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ export const {
VITE_CRX_VERSION_API,
VITE_AUTO_LOCK_IN_MINUTES,
VITE_SENTRY_DSN,
VITE_EXPLORER_URL,
VITE_ECOSYSTEM_PROJECTS_URL,
NODE_ENV,
} = import.meta.env;

export const EXPLORER_URL = VITE_EXPLORER_URL;
export const ECOSYSTEM_PROJECTS_URL = VITE_ECOSYSTEM_PROJECTS_URL;
export const WALLET_NAME = VITE_CRX_NAME;
export const APP_VERSION = VITE_APP_VERSION;
export const DATABASE_VERSION = Number(VITE_DATABASE_VERSION);
Expand Down
18 changes: 10 additions & 8 deletions packages/app/src/networks.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import type { NetworkData } from '@fuel-wallet/types';
import { CHAIN_IDS } from 'fuels';

export const IGNITION_NETWORK: NetworkData = {
name: 'Ignition',
url: 'https://mainnet.fuel.network/v1/graphql',
chainId: CHAIN_IDS.fuel.mainnet,
explorerUrl: 'https://app.fuel.network',
bridgeUrl: 'https://app.fuel.network/bridge',
isSelected: true,
};

export const DEFAULT_NETWORKS: Array<
NetworkData & { faucetUrl?: string; bridgeUrl?: string; hidden?: boolean }
> = [
{
name: 'Ignition',
url: 'https://mainnet.fuel.network/v1/graphql',
chainId: CHAIN_IDS.fuel.mainnet,
explorerUrl: 'https://app.fuel.network',
bridgeUrl: 'https://app.fuel.network/bridge',
isSelected: true,
},
IGNITION_NETWORK,
{
name: 'Fuel Sepolia Testnet',
url: 'https://testnet.fuel.network/v1/graphql',
Expand Down
22 changes: 22 additions & 0 deletions packages/app/src/systems/Contract/hooks/useContractMetadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Contract } from '@fuel-wallet/types';
import { Services, store } from '~/store';
import type { ContractsMachineState } from '../machines/contractsMachine';

const selectors = {
contract(id: string) {
return (state: ContractsMachineState): Contract | undefined => {
return state.context.contracts?.find(
(contract) => contract.contractId === id
);
};
},
};

export const useContractMetadata = (id: string): Contract | undefined => {
const contract = store.useSelector(
Services.contracts,
selectors.contract(id)
);

return contract;
};
81 changes: 81 additions & 0 deletions packages/app/src/systems/Contract/machines/contractsMachine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import type { Contract, EcosystemProject } from '@fuel-wallet/types';
import type { InterpreterFrom, StateFrom } from 'xstate';
import { assign, createMachine } from 'xstate';
import { FetchMachine } from '~/systems/Core';

import { EcosystemService } from '~/systems/Ecosystem/services/ecosystem';
import { ContractService } from '../services/contracts';

export type MachineContext = {
contracts?: Contract[];
};

type MachineServices = {
fetchProjects: {
data: EcosystemProject[];
};
};

export const contractsMachine = createMachine(
{
predictableActionArguments: true,

tsTypes: {} as import('./contractsMachine.typegen').Typegen0,
schema: {
context: {} as MachineContext,
services: {} as MachineServices,
},
id: '(machine)',
initial: 'fetching',
states: {
fetching: {
invoke: {
src: 'fetchProjects',
onDone: [
{
target: 'failure',
cond: FetchMachine.hasError,
},
{
target: 'success',
actions: ['assignContracts'],
},
],
},
},
failure: {
after: {
10000: 'fetching',
},
},
success: {
type: 'final',
},
},
},
{
actions: {
assignContracts: assign({
contracts: (_, ev) => {
return ContractService.formatContractsFromEcosystem(ev.data);
},
}),
},
services: {
fetchProjects: FetchMachine.create<
null,
MachineServices['fetchProjects']['data']
>({
showError: false,
maxAttempts: 1,
async fetch() {
return EcosystemService.fetchProjects();
},
}),
},
}
);

export type ContractsMachine = typeof contractsMachine;
export type ContractsMachineState = StateFrom<ContractsMachine>;
export type ContractsMachineService = InterpreterFrom<ContractsMachine>;
24 changes: 24 additions & 0 deletions packages/app/src/systems/Contract/services/contracts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { Contract, EcosystemProject } from '@fuel-wallet/types';
import { CHAIN_IDS } from 'fuels';

// biome-ignore lint/complexity/noStaticOnlyClass: <explanation>
export class ContractService {
static formatContractsFromEcosystem(
projects: EcosystemProject[]
): Contract[] {
return projects.flatMap((project) => {
if (!project.contracts?.mainnet) {
return [];
}

return project.contracts.mainnet.map((contract) => {
return {
chainId: CHAIN_IDS.fuel.mainnet,
contractId: contract.id,
name: contract.name,
image: project.image,
};
});
});
}
}
6 changes: 3 additions & 3 deletions packages/app/src/systems/Core/utils/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import type {
import Dexie, { type DbEvents, type PromiseExtended, type Table } from 'dexie';
import 'dexie-observable';
import type { AssetFuel } from 'fuels';
import { IS_LOGGED_KEY } from '~/config';
import { createParallelDb } from '~/systems/Core/utils/databaseNoDexie';
import { Storage } from '~/systems/Core/utils/storage';
import type { TransactionCursor } from '~/systems/Transaction';
import { chromeStorage } from '../services/chromeStorage';
import { applyDbVersioning } from './databaseVersioning';
import { createParallelDb } from '~/systems/Core/utils/databaseNoDexie';
import { IS_LOGGED_KEY } from '~/config';
import { Storage } from '~/systems/Core/utils/storage';
import { saveToOPFS } from './opfs';

type FailureEvents = Extract<keyof DbEvents, 'close' | 'blocked'>;
Expand Down
16 changes: 16 additions & 0 deletions packages/app/src/systems/Ecosystem/services/ecosystem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { EcosystemProject } from '@fuel-wallet/types';
import { ECOSYSTEM_PROJECTS_URL } from '~/config';

// biome-ignore lint/complexity/noStaticOnlyClass: <explanation>
export class EcosystemService {
static async fetchProjects() {
const res = await fetch(ECOSYSTEM_PROJECTS_URL);

if (res.ok) {
const data: EcosystemProject[] = await res.json();
return data;
}

return [];
}
}
9 changes: 9 additions & 0 deletions packages/app/src/systems/Ecosystem/utils/getProjectImage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { IGNITION_NETWORK } from '~/networks';
import { urlJoin } from '~/systems/Core';

export const getProjectImage = (image: string) => {
return urlJoin(
IGNITION_NETWORK.explorerUrl,
`/api/ecosystem/asset/${image}/image`
);
};
2 changes: 2 additions & 0 deletions packages/app/src/systems/Store/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { overlayEvents } from '../Overlay/events';
import { unlockMachine } from '../Unlock';
import { unlockEvents } from '../Unlock/events';

import { contractsMachine } from '../Contract/machines/contractsMachine';
import type { StoreMachines } from './types';
import { Services } from './types';

Expand All @@ -33,6 +34,7 @@ export const store = store$
.addMachine(Services.accounts, () => accountsMachine)
.addMachine(Services.networks, () => networksMachine)
.addMachine(Services.assets, () => assetsMachine)
.addMachine(Services.contracts, () => contractsMachine)
.addMachine(Services.unlock, () => unlockMachine)
.addMachine(Services.msgRequest, () => messageRequestMachine)
.addMachine(Services.connectRequest, () => connectRequestMachine)
Expand Down
3 changes: 3 additions & 0 deletions packages/app/src/systems/Store/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { StoreClass } from '@fuels/react-xstore';

import type { AccountsMachine } from '../Account';
import type { AssetsMachine } from '../Asset';
import type { ContractsMachine } from '../Contract/machines/contractsMachine';
import type {
AddAssetMachine,
ConnectRequestMachine,
Expand All @@ -19,6 +20,7 @@ export enum Services {
accounts = 'accounts',
networks = 'networks',
assets = 'assets',
contracts = 'contracts',
overlay = 'overlay',
unlock = 'unlock',
txRequest = 'txRequest',
Expand All @@ -33,6 +35,7 @@ export type StoreMachines = {
accounts: AccountsMachine;
networks: NetworksMachine;
assets: AssetsMachine;
contracts: ContractsMachine;
overlay: OverlayMachine;
unlock: UnlockMachine;
txRequest: TransactionRequestMachine;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { cssObj } from '@fuel-ui/css';
import { Avatar, Box, Card, Heading, Icon, Text } from '@fuel-ui/react';
import { Avatar, Box, Card, Heading, Icon, Image, Text } from '@fuel-ui/react';
import type { OperationTransactionAddress } from 'fuels';
import { Address, AddressType, ChainName, isB256, isBech32 } from 'fuels';
import type { FC } from 'react';
import { type FC, useMemo } from 'react';
import { EthAddress, FuelAddress, useAccounts } from '~/systems/Account';

import { useContractMetadata } from '~/systems/Contract/hooks/useContractMetadata';
import { TxRecipientCardLoader } from './TxRecipientCardLoader';
import { TxRecipientContractLogo } from './TxRecipientContractLogo';

export type TxRecipientCardProps = {
recipient?: OperationTransactionAddress;
Expand All @@ -29,8 +31,16 @@ export const TxRecipientCard: TxRecipientCardComponent = ({
const isContract = recipient?.type === AddressType.contract;
const isEthChain = recipient?.chain === ChainName.ethereum;
const isNetwork = address === 'Network';
const name =
accounts?.find((a) => a.address === fuelAddress)?.name || 'unknown';

const contract = useContractMetadata(address);

const name = useMemo<string>(() => {
if (isContract) {
return contract?.name || 'unknown';
}

return accounts?.find((a) => a.address === fuelAddress)?.name || 'unknown';
}, [isContract, contract, accounts, fuelAddress]);

return (
<Card
Expand Down Expand Up @@ -72,7 +82,11 @@ export const TxRecipientCard: TxRecipientCardComponent = ({
)}
{isContract && !isNetwork && (
<Box css={styles.iconWrapper}>
<Icon icon={Icon.is('Code')} size={20} />
<TxRecipientContractLogo
name={contract?.name}
image={contract?.image}
size={56}
/>
</Box>
)}
<Box.Flex css={styles.info}>
Expand Down Expand Up @@ -133,6 +147,7 @@ const styles = {
alignItems: 'center',
background: '$intentsBase3',
borderRadius: '$full',
overflow: 'hidden',
}),
info: cssObj({
flexDirection: 'column',
Expand Down
Loading

0 comments on commit 9cb81db

Please sign in to comment.