Skip to content

Commit 46ad8cd

Browse files
authored
takeOffer for dataLayer (#2879)
* takeOffer for dataLayer * Allow fee input * Fixed incomplete type guard * AlertDialog to ConfirmDialog * Added comments * Consolidate submitHandler behavior * Align datalayer offer detection logic to the backend * Fixed a minor issue
1 parent 822453e commit 46ad8cd

13 files changed

Lines changed: 429 additions & 22 deletions
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export type SingletonDependency = {
2+
launcherId: string;
3+
valuesToProve: string[];
4+
};
5+
6+
export type SingletonSummaryRecord = {
7+
launcherId: string;
8+
newRoot: string;
9+
dependencies: SingletonDependency[];
10+
};
11+
12+
type DataLayerOfferSummary = {
13+
offered: SingletonSummaryRecord[];
14+
};
15+
16+
export default DataLayerOfferSummary;

packages/api/src/@types/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ export type { default as Coin } from './Coin';
1212
export type { default as Coin2 } from './Coin2';
1313
export type { default as CoinSolution } from './CoinSolution';
1414
export type { default as Connection } from './Connection';
15+
export type {
16+
default as DataLayerOfferSummary,
17+
SingletonSummaryRecord,
18+
SingletonDependency,
19+
} from './DataLayerOfferSummary';
1520
export type { default as FarmingInfo } from './FarmingInfo';
1621
export type { default as NewFarmingInfo } from './NewFarmingInfo';
1722
export type { default as Fingerprint } from './Fingerprint';

packages/api/src/services/WalletService.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import BigNumber from 'bignumber.js';
22

33
import type AutoClaim from '../@types/AutoClaim';
44
import type Connection from '../@types/Connection';
5+
import type DataLayerOfferSummary from '../@types/DataLayerOfferSummary';
56
import type FarmedAmount from '../@types/FarmedAmount';
67
import type OfferSummaryRecord from '../@types/OfferSummaryRecord';
78
import type PoolWalletStatus from '../@types/PoolWalletStatus';
@@ -284,7 +285,7 @@ export default class Wallet extends Service {
284285
}
285286

286287
async getOfferSummary({ offerData }: { offerData: string }) {
287-
return this.command<{ id: string; summary: OfferSummaryRecord }>('get_offer_summary', {
288+
return this.command<{ id: string; summary: OfferSummaryRecord | DataLayerOfferSummary }>('get_offer_summary', {
288289
offer: offerData,
289290
});
290291
}

packages/gui/src/components/notification/NotificationPreviewOffer.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import React, { useMemo } from 'react';
44
import Notification from '../../@types/Notification';
55
import NotificationType from '../../constants/NotificationType';
66
import useOffer from '../../hooks/useOffer';
7+
import isDataLayerOfferSummary from '../../util/isDataLayerOfferSummary';
78
import offerToOfferBuilderData from '../../util/offerToOfferBuilderData';
89

910
import NotificationPreviewNFT from './NotificationPreviewNFT';
@@ -34,11 +35,11 @@ export default function NotificationPreviewOffer(props: NotificationPreviewOffer
3435
const { offer, isLoading } = useOffer(offerURLOrData);
3536

3637
const nft = useMemo(() => {
37-
if (!offer || !offer.summary) {
38+
if (!offer || !offer.summary || isDataLayerOfferSummary(offer.summary)) {
3839
return null;
3940
}
4041

41-
const offerBuilderData = offerToOfferBuilderData(offer?.summary);
42+
const offerBuilderData = offerToOfferBuilderData(offer.summary);
4243
const side = requested ? offerBuilderData.offered : offerBuilderData.requested;
4344

4445
const [firstNFT] = side.nfts ?? [];

packages/gui/src/components/offers/OfferImport.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type OfferSummaryRecord } from '@chia-network/api';
1+
import { type DataLayerOfferSummary, type OfferSummaryRecord } from '@chia-network/api';
22
import { useGetOfferSummaryMutation } from '@chia-network/api-react';
33
import {
44
Back,
@@ -14,6 +14,8 @@ import { Button, Grid, Typography } from '@mui/material';
1414
import React from 'react';
1515
import { useHotkeys } from 'react-hotkeys-hook';
1616

17+
import isDataLayerOfferSummary from '../../util/isDataLayerOfferSummary';
18+
1719
import OfferDataEntryDialog from './OfferDataEntryDialog';
1820
import { offerContainsAssetOfType } from './utils';
1921

@@ -34,7 +36,7 @@ function SelectOfferFile() {
3436

3537
async function parseOfferSummary(rawOfferData: string, offerFilePath: string | undefined) {
3638
const [offerData /* , leadingText, trailingText */] = parseOfferData(rawOfferData);
37-
let offerSummary: OfferSummaryRecord | undefined;
39+
let offerSummary: OfferSummaryRecord | DataLayerOfferSummary | undefined;
3840

3941
if (offerData) {
4042
const { data: response } = await getOfferSummary({ offerData });
@@ -48,9 +50,14 @@ function SelectOfferFile() {
4850
}
4951

5052
if (offerSummary) {
51-
const navigationPath = offerContainsAssetOfType(offerSummary, 'singleton')
52-
? '/dashboard/offers/view-nft'
53-
: '/dashboard/offers/view';
53+
let navigationPath: string;
54+
if (isDataLayerOfferSummary(offerSummary)) {
55+
navigationPath = '/dashboard/offers/view';
56+
} else {
57+
navigationPath = offerContainsAssetOfType(offerSummary, 'singleton')
58+
? '/dashboard/offers/view-nft'
59+
: '/dashboard/offers/view';
60+
}
5461

5562
navigate(navigationPath, {
5663
state: { offerData, offerSummary, offerFilePath, imported: true },

packages/gui/src/components/offers/utils.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ export function offerContainsAssetOfType(
165165
side?: 'offered' | 'requested',
166166
): boolean {
167167
const { infos } = offerSummary;
168+
if (!infos) {
169+
return false;
170+
}
168171
const matchingAssetIds: string[] = Object.keys(infos).filter((assetId) => {
169172
const info: OfferSummaryAssetInfo = infos[assetId];
170173
return info.type === assetType;
@@ -192,6 +195,10 @@ export function offerAssetTypeForAssetId(assetId: string, offerSummary: OfferSum
192195
assetType = OfferAsset.CHIA;
193196
} else {
194197
const { infos } = offerSummary;
198+
// DataLayerOfferSummary has no infos field
199+
if (!infos) {
200+
return undefined;
201+
}
195202
const info: OfferSummaryAssetInfo = infos[assetId];
196203

197204
if (info) {
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import type { DataLayerOfferSummary, SingletonSummaryRecord } from '@chia-network/api';
2+
import { Color, CopyToClipboard, Flex, Truncate, truncateValue } from '@chia-network/core';
3+
import { Trans } from '@lingui/macro';
4+
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
5+
import StorageIcon from '@mui/icons-material/Storage';
6+
import VerifiedIcon from '@mui/icons-material/Verified';
7+
import {
8+
Box,
9+
Chip,
10+
Divider,
11+
Table,
12+
TableBody,
13+
TableCell,
14+
TableContainer,
15+
TableHead,
16+
TableRow,
17+
Typography,
18+
} from '@mui/material';
19+
import { useTheme } from '@mui/material/styles';
20+
import React from 'react';
21+
22+
export type DataLayerOfferViewerProps = {
23+
summary: DataLayerOfferSummary;
24+
};
25+
26+
function StoreUpdateCard({ entry, index }: { entry: SingletonSummaryRecord; index: number }) {
27+
const theme = useTheme();
28+
29+
return (
30+
<Box
31+
sx={{
32+
borderRadius: 1,
33+
p: 3,
34+
backgroundColor: 'background.card',
35+
border: '1px solid',
36+
borderColor: theme.palette.mode === 'light' ? Color.Neutral[300] : Color.Neutral[600],
37+
}}
38+
>
39+
<Flex flexDirection="column" gap={2}>
40+
<Flex alignItems="center" gap={1}>
41+
<StorageIcon fontSize="small" color="primary" />
42+
<Typography variant="subtitle2">
43+
<Trans>Store {index + 1}</Trans>
44+
</Typography>
45+
</Flex>
46+
47+
<Flex flexDirection="row" alignItems="center" gap={1}>
48+
<Typography variant="body2" color="textSecondary" sx={{ minWidth: 80 }}>
49+
<Trans>Store ID</Trans>
50+
</Typography>
51+
<Chip label={truncateValue(entry.launcherId, {})} size="small" variant="outlined" />
52+
<CopyToClipboard value={entry.launcherId} />
53+
</Flex>
54+
55+
<Flex flexDirection="row" alignItems="center" gap={1}>
56+
<Typography variant="body2" color="textSecondary" sx={{ minWidth: 80 }}>
57+
<Trans>New Root</Trans>
58+
</Typography>
59+
<Flex alignItems="center" gap={0.5}>
60+
<ArrowForwardIcon fontSize="small" color="action" />
61+
<Chip label={truncateValue(entry.newRoot, {})} size="small" color="primary" variant="outlined" />
62+
<CopyToClipboard value={entry.newRoot} />
63+
</Flex>
64+
</Flex>
65+
</Flex>
66+
</Box>
67+
);
68+
}
69+
70+
function RequiredProofsTable({ entries }: { entries: SingletonSummaryRecord[] }) {
71+
const allDeps = entries.flatMap((entry) =>
72+
entry.dependencies.map((dep) => ({
73+
sourceStoreId: entry.launcherId,
74+
dependsOnStoreId: dep.launcherId,
75+
valuesToProve: dep.valuesToProve,
76+
})),
77+
);
78+
79+
if (allDeps.length === 0) return null;
80+
81+
return (
82+
<Box>
83+
<Flex alignItems="center" gap={1} sx={{ mb: 1.5 }}>
84+
<VerifiedIcon fontSize="small" color="info" />
85+
<Typography variant="subtitle2">
86+
<Trans>Required Proofs</Trans>
87+
</Typography>
88+
</Flex>
89+
<Typography variant="body2" color="textSecondary" sx={{ mb: 2 }}>
90+
<Trans>
91+
The following values must be proven to exist in the specified stores before this offer can be accepted.
92+
</Trans>
93+
</Typography>
94+
<TableContainer>
95+
<Table size="small">
96+
<TableHead>
97+
<TableRow>
98+
<TableCell>
99+
<Trans>For Store Update</Trans>
100+
</TableCell>
101+
<TableCell>
102+
<Trans>Requires Proof From Store</Trans>
103+
</TableCell>
104+
<TableCell>
105+
<Trans>Values to Prove</Trans>
106+
</TableCell>
107+
</TableRow>
108+
</TableHead>
109+
<TableBody>
110+
{allDeps.map((dep) => (
111+
<TableRow key={`${dep.sourceStoreId}-${dep.dependsOnStoreId}`}>
112+
<TableCell>
113+
<Flex alignItems="center" gap={0.5}>
114+
<Truncate tooltip>{dep.sourceStoreId}</Truncate>
115+
<CopyToClipboard value={dep.sourceStoreId} />
116+
</Flex>
117+
</TableCell>
118+
<TableCell>
119+
<Flex alignItems="center" gap={0.5}>
120+
<Truncate tooltip>{dep.dependsOnStoreId}</Truncate>
121+
<CopyToClipboard value={dep.dependsOnStoreId} />
122+
</Flex>
123+
</TableCell>
124+
<TableCell>
125+
<Flex flexDirection="column" gap={0.5}>
126+
{dep.valuesToProve.map((v) => (
127+
<Flex key={v} alignItems="center" gap={0.5}>
128+
<Truncate tooltip>{v}</Truncate>
129+
<CopyToClipboard value={v} />
130+
</Flex>
131+
))}
132+
</Flex>
133+
</TableCell>
134+
</TableRow>
135+
))}
136+
</TableBody>
137+
</Table>
138+
</TableContainer>
139+
</Box>
140+
);
141+
}
142+
143+
export default function DataLayerOfferViewer(props: DataLayerOfferViewerProps) {
144+
const { summary } = props;
145+
146+
return (
147+
<Flex flexDirection="column" gap={3}>
148+
<Typography variant="h6">
149+
<Trans>Data Layer Offer</Trans>
150+
</Typography>
151+
<Typography variant="body2" color="textSecondary">
152+
<Trans>
153+
This offer proposes updates to Data Layer store roots. Accepting it will transition the listed stores to new
154+
roots. Dependencies specify values that must be proven to exist in other stores before acceptance.
155+
</Trans>
156+
</Typography>
157+
158+
<Divider />
159+
160+
<Box>
161+
<Flex alignItems="center" gap={1} sx={{ mb: 1.5 }}>
162+
<StorageIcon fontSize="small" color="primary" />
163+
<Typography variant="subtitle1">
164+
<Trans>Offered Store Updates ({summary.offered.length})</Trans>
165+
</Typography>
166+
</Flex>
167+
<Typography variant="body2" color="textSecondary" sx={{ mb: 2 }}>
168+
<Trans>These stores will have their roots updated to the specified values.</Trans>
169+
</Typography>
170+
<Flex flexDirection="column" gap={2}>
171+
{summary.offered.map((entry, i) => (
172+
<StoreUpdateCard key={entry.launcherId} entry={entry} index={i} />
173+
))}
174+
</Flex>
175+
</Box>
176+
177+
<Divider />
178+
179+
<RequiredProofsTable entries={summary.offered} />
180+
</Flex>
181+
);
182+
}

0 commit comments

Comments
 (0)