Skip to content

Commit 1e42a42

Browse files
authored
FLIX v1.1 (#234)
* working flix v1.1 for the most part * sort accounts in address builder by address * fix type errors * fix cadence code building * fix type error
1 parent 4a44726 commit 1e42a42

File tree

19 files changed

+315
-148
lines changed

19 files changed

+315
-148
lines changed

apps/electron/src/renderer/App.tsx

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ import {
1616
} from '@onflowser/api';
1717
import { SWRConfig } from 'swr';
1818
import { FlowNetworkProvider } from '@onflowser/ui/src/contexts/flow-network.context';
19+
import { FlowFlixV11Service } from '@onflowser/core/src/flow-flix-v11.service';
20+
import { HttpService } from '@onflowser/core/src/http.service';
21+
import { IFlowserLogger } from '@onflowser/core';
1922
import { DependencyErrors } from './components/DependencyErrors/DependencyErrors';
20-
import { indexSyncIntervalInMs, IpcIndexCache } from './ipc-index-cache';
2123
import { FlowserRouter } from './router';
2224
import { UpdateLoader } from './components/loaders/UpdateLoader';
2325
import { AnalyticsService } from '../services/analytics.service';
2426
import { SentryRendererService } from '../services/sentry-renderer.service';
27+
import { indexSyncIntervalInMs, IpcIndexCache } from './ipc-index-cache';
2528

2629
const indexes = {
2730
accountStorage: new IpcIndexCache<FlowAccountStorage>('accountStorage'),
@@ -36,6 +39,28 @@ const indexes = {
3639
const analyticsService = new AnalyticsService();
3740
const monitoringService = new SentryRendererService();
3841

42+
class WebLogger implements IFlowserLogger {
43+
debug(message: any): void {
44+
console.debug(message);
45+
}
46+
47+
error(message: any, error?: unknown): void {
48+
console.error(message, error);
49+
}
50+
51+
log(message: any): void {
52+
console.log(message);
53+
}
54+
55+
verbose(message: any): void {
56+
console.debug(message);
57+
}
58+
59+
warn(message: any): void {
60+
console.warn(message);
61+
}
62+
}
63+
3964
export function App() {
4065
const [isExiting, setIsExiting] = useState(false);
4166
const [appUpdateDownloadPercentage, setAppUpdateDownloadPercentage] =
@@ -97,6 +122,12 @@ export function App() {
97122
analyticsService,
98123
workspaceService: window.electron.workspaces,
99124
snapshotService: window.electron.snapshots,
125+
flixService: new FlowFlixV11Service(
126+
{
127+
flixServerUrl: 'https://flix-indexer.fly.dev',
128+
},
129+
new HttpService(new WebLogger()),
130+
),
100131
}}
101132
>
102133
<ConfirmDialogProvider>

apps/electron/src/services/flowser-app.service.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,8 @@ export class FlowserAppService {
234234
message: errorMessage,
235235
detail: isErrorWithMessage(error) ? error.message : undefined,
236236
type: 'error',
237-
buttons: ['Restart app'],
237+
cancelId: 1,
238+
buttons: ['Restart app', 'Cancel'],
238239
});
239240
const quitClicked = result.response === 0;
240241
if (quitClicked) {

apps/web/src/app/[networkId]/[interaction]/opengraph-image.tsx

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { ImageResponse } from 'next/og';
2-
import { FlixUtils } from "@onflowser/core/src/flix-utils";
2+
import { FlixV11Utils } from "@onflowser/core/src/flix-v11-utils";
33
import { FlowUtils } from "@onflowser/core/src/flow-utils";
44
import type { FlowNetworkId } from "@onflowser/core/src/flow-utils";
55
import type { FlowNameProfile } from "@onflowser/core/src/flow-names.service";
6-
import type { FlixV1Auditor, FlixV1Template } from "@onflowser/core/src/flix-v1";
76
import type { InteractionsPageParams } from "@/common/interaction-page-params";
7+
import type { FlixV11Template } from "@onflowser/core/src/flix-v11";
8+
import { FlixV1Auditor } from "@onflowser/core";
89

910
// Don't use edge runtime because of the code size limit.
1011
// See: https://github.com/vercel/commerce/issues/1028
@@ -31,17 +32,18 @@ export default async function Image(props: Props) {
3132
])
3233

3334
if (interaction) {
34-
const [flix, auditors] = await Promise.all([
35+
const [flix] = await Promise.all([
3536
fetchFlixTemplateById(interaction),
36-
fetchFlixAuditorsById(interaction, networkId),
37+
// Auditing not implemented yet
38+
// fetchFlixAuditorsById(interaction, networkId),
3739
]);
3840

3941
if (!flix) {
4042
return new ImageResponse(<FallbackPreview />, size);
4143
}
4244

43-
const cadenceSourceCode = FlixUtils.getCadenceSourceCode(flix, networkId);
44-
const dependencies = FlixUtils.getDependencies(flix, networkId);
45+
const cadenceSourceCode = FlixV11Utils.getCadenceSourceCode(flix, networkId);
46+
const dependencies = FlixV11Utils.getDependencies(flix, networkId);
4547
const address = dependencies[0].address;
4648

4749
const [flowNameProfiles, addressIndex] = await Promise.all([
@@ -71,8 +73,8 @@ export default async function Image(props: Props) {
7173
}}
7274
>
7375
<TitleAndDescription flix={flix} />
74-
<AuditorInfo auditors={auditors} />
75-
<CodePreview cadence={cadenceSourceCode} />
76+
{/*<AuditorInfo auditors={auditors} />*/}
77+
{cadenceSourceCode && <CodePreview cadence={cadenceSourceCode} />}
7678
</div>
7779
<div
7880
style={{
@@ -115,7 +117,7 @@ function FallbackPreview() {
115117
)
116118
}
117119

118-
function TitleAndDescription(props: {flix: FlixV1Template}) {
120+
function TitleAndDescription(props: {flix: FlixV11Template}) {
119121
return (
120122
<div
121123
style={{
@@ -132,7 +134,7 @@ function TitleAndDescription(props: {flix: FlixV1Template}) {
132134
color: "#31363C",
133135
}}
134136
>
135-
{FlixUtils.getName(props.flix)}
137+
{FlixV11Utils.getName(props.flix)}
136138
</span>
137139
<span
138140
style={{
@@ -141,12 +143,13 @@ function TitleAndDescription(props: {flix: FlixV1Template}) {
141143
color: "#56595E",
142144
}}
143145
>
144-
{FlixUtils.getDescription(props.flix)}
146+
{FlixV11Utils.getDescription(props.flix)}
145147
</span>
146148
</div>
147149
)
148150
}
149151

152+
// TODO(flix-v11): Add support for auditors?
150153
function AuditorInfo(props: { auditors: FlixV1Auditor[]}) {
151154
const {auditors} = props;
152155
const iconSize = 25;
@@ -292,19 +295,20 @@ function RiStarFill(props: IconProps) {
292295
// and axios can't be used in edge runtime.
293296
// See: https://github.com/axios/axios/issues/5523
294297

295-
const flixApiHost = `https://flowser-flix-368a32c94da2.herokuapp.com`;
298+
const flixApiHost = `https://flix-indexer.fly.dev`;
296299

297-
async function fetchFlixTemplateById(id: string): Promise<FlixV1Template | undefined> {
298-
const res = await fetch(`${flixApiHost}/v1/templates/${id}`);
300+
async function fetchFlixTemplateById(id: string): Promise<FlixV11Template | undefined> {
301+
const res = await fetch(`${flixApiHost}/v1.1/templates/${id}`);
299302
if (res.status === 200) {
300303
return await res.json();
301304
} else {
302305
return undefined;
303306
}
304307
}
305308

309+
// TODO(flix-v11) Migrate to flix v1.1
306310
async function fetchFlixAuditorsById(id: string, networkId: string): Promise<FlixV1Auditor[]> {
307-
const res = await fetch(`${flixApiHost}/v1/templates/${id}/auditors?network=${networkId}`);
311+
const res = await fetch(`${flixApiHost}/v1.1/templates/${id}/auditors?network=${networkId}`);
308312
return await res.json();
309313
}
310314

apps/web/src/common/metadata.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { Metadata } from "next";
22
import { HttpService } from "@onflowser/core/src/http.service";
3-
import { FlixUtils, FlowFlixService } from "@onflowser/core";
43
import { InteractionsPageParams } from "@/common/interaction-page-params";
4+
import { FlowFlixV11Service } from "@onflowser/core/src/flow-flix-v11.service";
5+
import { FlixV11Utils } from "@onflowser/core/src/flix-v11-utils";
56

67
export async function getInteractionPageMetadata(
78
params: InteractionsPageParams
@@ -21,15 +22,15 @@ async function getFlixMetadata(flixId: string): Promise<Metadata> {
2122
verbose: console.debug
2223
});
2324
const flixConfig = {
24-
flixServerUrl: "https://flowser-flix-368a32c94da2.herokuapp.com"
25+
flixServerUrl: "https://flix-indexer.fly.dev"
2526
};
26-
const flixService = new FlowFlixService(flixConfig, httpService);
27+
const flixService = new FlowFlixV11Service(flixConfig, httpService);
2728

2829
const template = await flixService.getById(flixId);
2930

3031
if (template) {
31-
const title = `Interaction: ${FlixUtils.getName(template)}`;
32-
const description = FlixUtils.getDescription(template)
32+
const title = `Interaction: ${FlixV11Utils.getName(template)}`;
33+
const description = FlixV11Utils.getDescription(template)
3334

3435
return {
3536
title,

apps/web/src/common/root.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import { NetworkDropdown } from "./NetworkDropdown";
5151
import { ProfileDropdown } from "@/common/ProfileDropdown";
5252
import { ConfirmDialogProvider } from "@onflowser/ui/src/contexts/confirm-dialog.context";
5353
import { Analytics } from "@vercel/analytics/react"
54+
import { FlowFlixV11Service } from "@onflowser/core/src/flow-flix-v11.service";
5455

5556
const indexSyncIntervalInMs = 500;
5657

@@ -352,6 +353,9 @@ export default function Root() {
352353
<ServiceRegistryProvider
353354
services={{
354355
flowService: new FlowService(),
356+
flixService: new FlowFlixV11Service({
357+
flixServerUrl:"https://flix-indexer.fly.dev"
358+
}, appService.httpService),
355359
interactionsService: appService.interactionsService,
356360
transactionsIndex: appService.getTransactionIndex(),
357361
blocksIndex: appService.getBlockIndex(),

packages/core/src/flix-utils.ts renamed to packages/core/src/flix-v1-utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ type FlixDependencySummary = {
66
address: string;
77
}
88

9-
export class FlixUtils {
9+
export class FlixV1Utils {
1010

1111
static getDependencies(template: FlixV1Template, networkId: FlowNetworkId): FlixDependencySummary[] {
1212
return Object

packages/core/src/flix-v1.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { FlowNetworkId } from "./flow-utils";
44
export type FlixV1Template = {
55
id: string;
66
f_type: "InteractionTemplate";
7-
f_version: string;
7+
f_version: "1.0.0";
88
data: {
99
messages: FlixV1Messages;
1010
dependencies: Record<string, FlixV1Dependency>;

packages/core/src/flix-v11-utils.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import type { FlowNetworkId } from "./flow-utils";
2+
import type { FlixV11Template } from "./flix-v11";
3+
import { ensurePrefixedAddress, isDefined } from "./utils";
4+
5+
type FlixDependencySummary = {
6+
name: string;
7+
address: string;
8+
}
9+
10+
export class FlixV11Utils {
11+
12+
static getDependencies(template: FlixV11Template, networkId: FlowNetworkId): FlixDependencySummary[] {
13+
return template.data.dependencies
14+
?.map(e => e.contracts)
15+
.flat()
16+
.map((dependency): FlixDependencySummary | undefined => {
17+
const network = dependency.networks.find(network => network.network === networkId);
18+
19+
if (!network) {
20+
return undefined;
21+
}
22+
23+
return {
24+
name: dependency.contract,
25+
address: network.address
26+
}
27+
})
28+
.filter(isDefined) ?? [];
29+
}
30+
31+
static getDescription(template: FlixV11Template): string | undefined {
32+
return template.data.messages
33+
?.find(message => message.key === "description")?.i18n
34+
?.find(i18n => i18n.tag === "en-US")?.translation;
35+
}
36+
37+
static getName(template: FlixV11Template): string {
38+
const englishTitle = template.data.messages
39+
?.find(message => message.key === "title")?.i18n
40+
?.find(i18n => i18n.tag === "en-US")?.translation;
41+
return englishTitle ?? "Unknown";
42+
}
43+
44+
static getCadenceSourceCode(template: FlixV11Template, networkId: FlowNetworkId): string | undefined {
45+
let source = template.data.cadence.body;
46+
const contractDependency = template.data.dependencies?.flatMap(e => e.contracts) ?? [];
47+
for (const dependency of contractDependency) {
48+
const networkConfig = dependency.networks.find(network => network.network === networkId);
49+
if (!networkConfig) {
50+
return undefined;
51+
}
52+
53+
source = source.replace(
54+
new RegExp(`import +"${dependency.contract}"`),
55+
`import ${dependency.contract} from ${ensurePrefixedAddress(networkConfig.address)}`
56+
);
57+
}
58+
return source;
59+
}
60+
61+
static hasDependenciesForNetwork(template: FlixV11Template, networkId: FlowNetworkId): boolean {
62+
return template.data.dependencies
63+
?.map(e => e.contracts)
64+
.flat()
65+
.some(dependency =>
66+
dependency.networks.some(network => network.network === networkId)
67+
) ?? false;
68+
}
69+
70+
}

packages/core/src/flix-v11.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// https://github.com/onflow/flips/blob/main/application/20220503-interaction-templates.md#interaction-interfaces
2+
import { FlowNetworkId } from "./flow-utils";
3+
4+
export type FlixV11Template = {
5+
id: string;
6+
f_type: "InteractionTemplate";
7+
f_version: "1.1.0";
8+
data: {
9+
messages: FlixV11Message[] | null;
10+
cadence: {
11+
body: string;
12+
network_pins: FlixV11NetworkPin[];
13+
};
14+
parameters: FlixV11Parameter[];
15+
dependencies: { contracts: FlixV11ContractDependency[] }[] | null;
16+
};
17+
};
18+
19+
export type FlixV11ContractDependency = {
20+
// Name of the contract (e.g. "EVM")
21+
contract: string;
22+
networks: FlixV11ContractNetwork[];
23+
}
24+
25+
export type FlixV11ContractNetwork = {
26+
network: FlowNetworkId;
27+
address: string;
28+
}
29+
30+
export type FlixV11NetworkPin = {
31+
network: FlowNetworkId;
32+
pin_self: string;
33+
}
34+
35+
export type FlixV11Message = {
36+
// Standard keys: "title", "description",..
37+
key: string;
38+
i18n: FlixV11MessageI18n[];
39+
}
40+
41+
export type FlixV11MessageI18n = {
42+
// Language key (e.g. "en-US")
43+
tag: string;
44+
translation: string;
45+
}
46+
47+
export type FlixV11Parameter = {
48+
label: string;
49+
index: 0;
50+
type: string;
51+
messages: FlixV11Message[];
52+
}

packages/core/src/flow-flix.service.ts renamed to packages/core/src/flow-flix-v1.service.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { HttpService } from "./http.service";
22
import { FlixV1Template } from "./flix-v1";
33

4-
type FlowV1FlixServiceConfig = {
4+
type FlowFlixV1ServiceConfig = {
55
flixServerUrl: string;
66
}
77

8-
export class FlowFlixService {
8+
export class FlowFlixV1Service {
99
constructor(
10-
private readonly config: FlowV1FlixServiceConfig,
10+
private readonly config: FlowFlixV1ServiceConfig,
1111
private readonly httpService: HttpService
1212
) {}
1313

0 commit comments

Comments
 (0)