Skip to content

Commit 959aed8

Browse files
committed
Merge branch 'develop' into dw/binding-cacher
2 parents aab0614 + 9285d13 commit 959aed8

30 files changed

Lines changed: 753 additions & 339 deletions

File tree

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "hyperdrive_lib"
33
authors = ["Sybil Technologies AG"]
4-
version = "1.7.1"
4+
version = "1.8.1"
55
edition = "2021"
66
description = "A general-purpose sovereign cloud computing platform"
77
homepage = "https://hyperware.ai"

hyperdrive/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "hyperdrive"
33
authors = ["Sybil Technologies AG"]
4-
version = "1.7.1"
4+
version = "1.8.1"
55
edition = "2021"
66
description = "A general-purpose sovereign cloud computing platform"
77
homepage = "https://hyperware.ai"

hyperdrive/packages/app-store/public-ui/src/components/AppDetail.tsx

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { useEffect, useState } from "react";
22
import { useParams } from "react-router-dom";
33
import type { AppListing } from "../types/app";
4-
import { FaChevronDown, FaChevronRight } from "react-icons/fa6";
4+
import { FaChevronDown, FaChevronRight, FaXmark } from "react-icons/fa6";
55
import classNames from "classnames";
66

77
const mockApp: AppListing = {
@@ -33,10 +33,11 @@ export default function AppDetail() {
3333
const [app, setApp] = useState<AppListing | null>(null);
3434
const [isLoading, setIsLoading] = useState(true);
3535
const [error, setError] = useState<string | null>(null);
36-
const [showScreenshots, setShowScreenshots] = useState(false);
36+
const [showScreenshots, setShowScreenshots] = useState(true);
3737
const [isDevMode, setIsDevMode] = useState(false);
3838
const [backtickPressCount, setBacktickPressCount] = useState(0);
3939
const [detailExpanded, setDetailExpanded] = useState(false);
40+
const [activeScreenshot, setActiveScreenshot] = useState<string | null>(null);
4041
useEffect(() => {
4142
const backTickCounter = (e: KeyboardEvent) => {
4243
if (e.key === '`') {
@@ -126,6 +127,9 @@ export default function AppDetail() {
126127

127128
const hasScreenshots = (app.metadata?.properties?.screenshots &&
128129
app.metadata.properties.screenshots.length > 0) || isDevMode;
130+
const screenshots = isDevMode
131+
? mockApp.metadata!.properties!.screenshots!
132+
: (app.metadata?.properties?.screenshots ?? []);
129133

130134
return (
131135
<div className="max-w-screen md:max-w-screen-md mx-auto flex flex-col items-stretch gap-4">
@@ -214,20 +218,45 @@ export default function AppDetail() {
214218

215219
{showScreenshots && (
216220
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
217-
{(isDevMode ? mockApp.metadata!.properties!.screenshots! : app.metadata?.properties?.screenshots || []).map((screenshot: string, index: number) => (
221+
{screenshots.map((screenshot: string, index: number) => (
218222
<img
219223
key={index}
220224
src={screenshot}
221225
alt={`Screenshot ${index + 1}`}
222226
className="rounded-lg w-full h-auto object-cover aspect-video max-h-64 hover:scale-105 transition-transform cursor-pointer"
223227
loading="lazy"
224-
onClick={() => window.open(screenshot, '_blank')}
228+
onClick={() => setActiveScreenshot(screenshot)}
225229
/>
226230
))}
227231
</div>
228232
)}
229233
</div>
230234
)}
235+
{activeScreenshot && (
236+
<div
237+
className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-4"
238+
onClick={() => setActiveScreenshot(null)}
239+
>
240+
<div
241+
className="relative max-w-5xl max-h-[90vh] flex items-center justify-center"
242+
onClick={event => event.stopPropagation()}
243+
>
244+
<button
245+
type="button"
246+
className="absolute top-2 right-2 bg-black/60 text-white rounded-full p-2 hover:bg-black/80 transition-colors"
247+
onClick={() => setActiveScreenshot(null)}
248+
aria-label="Close screenshot preview"
249+
>
250+
<FaXmark size={18} />
251+
</button>
252+
<img
253+
src={activeScreenshot}
254+
alt="Selected app screenshot"
255+
className="max-h-[85vh] w-auto max-w-full rounded-lg shadow-2xl"
256+
/>
257+
</div>
258+
</div>
259+
)}
231260

232261
<div className={classNames("bg-black/10 dark:bg-white/10 rounded-lg p-4 flex flex-col transition-all duration-300", {
233262
'gap-4': detailExpanded,

hyperdrive/packages/app-store/ui/src/abis/helpers.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { multicallAbi, hypermapAbi, mechAbi, HYPERMAP, MULTICALL, HYPER_ACCOUNT_UPGRADABLE_IMPL } from "./";
22
import { encodeFunctionData, encodePacked, stringToHex } from "viem";
33

4-
export function encodeMulticalls(metadataUri: string, metadataHash: string) {
4+
export function encodeMulticalls(metadataUri: string, metadataHash: string, tbaAddress?: `0x${string}`) {
55
const metadataHashCall = encodeFunctionData({
66
abi: hypermapAbi,
77
functionName: 'note',
@@ -20,11 +20,22 @@ export function encodeMulticalls(metadataUri: string, metadataHash: string) {
2020
]
2121
})
2222

23-
const calls = [
23+
// Add initialize call if TBA address is provided
24+
const initializeCall = tbaAddress ? encodeFunctionData({
25+
abi: [{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"}],
26+
functionName: 'initialize',
27+
args: []
28+
}) : null;
29+
30+
const baseCalls = [
2431
{ target: HYPERMAP, callData: metadataHashCall },
2532
{ target: HYPERMAP, callData: metadataUriCall },
2633
];
2734

35+
const calls = initializeCall && tbaAddress ?
36+
[{ target: tbaAddress, callData: initializeCall }, ...baseCalls] :
37+
baseCalls;
38+
2839
const multicall = encodeFunctionData({
2940
abi: multicallAbi,
3041
functionName: 'aggregate',

hyperdrive/packages/app-store/ui/src/abis/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ export const HYPERMAP: `0x${string}` = "0x000000000044C6B8Cb4d8f0F889a3E47664EAe
66
export const MULTICALL: `0x${string}` = "0xcA11bde05977b3631167028862bE2a173976CA11";
77
export const HYPER_ACCOUNT_IMPL: `0x${string}` = "0x0000000000EDAd72076CBe7b9Cfa3751D5a85C97";
88
export const HYPER_ACCOUNT_UPGRADABLE_IMPL: `0x${string}` = "0x0000000000691b70A051CFAF82F9622E150369f3";
9+
export const ERC6551_REGISTRY = '0x000000006551c19487814612e58FE06813775758';
10+
11+
// HyperAccountProxy creation code
12+
export const PROXY_CREATION_CODE = '0x60a0604052348015600e575f5ffd5b50604051610506380380610506833981016040819052602b916044565b6001600160a01b03166080525f805460ff19169055606f565b5f602082840312156053575f5ffd5b81516001600160a01b03811681146068575f5ffd5b9392505050565b6080516104806100865f395f606201526104805ff3fe608060405260043610610021575f3560e01c8063d1f578941461003257610028565b3661002857005b610030610045565b005b610030610040366004610363565b610057565b610055610050610111565b610148565b565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146100be575f5460ff161561009e57610099610045565b6100be565b60405163572190d160e01b81523360048201526024015b60405180910390fd5b5f5460ff16156100e05760405162dc149f60e41b815260040160405180910390fd5b5f6100e9610111565b6001600160a01b03160361010d575f805460ff1916600117905561010d8282610166565b5050565b5f6101437f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b365f5f375f5f365f845af43d5f5f3e808015610162573d5ff35b3d5ffd5b61016f826101c0565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156101b8576101b38282610236565b505050565b61010d6102a8565b806001600160a01b03163b5f036101f557604051634c9c8ce360e01b81526001600160a01b03821660048201526024016100b5565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f5f846001600160a01b0316846040516102529190610434565b5f60405180830381855af49150503d805f811461028a576040519150601f19603f3d011682016040523d82523d5f602084013e61028f565b606091505b509150915061029f8583836102c7565b95945050505050565b34156100555760405163b398979f60e01b815260040160405180910390fd5b6060826102dc576102d782610326565b61031f565b81511580156102f357506001600160a01b0384163b155b1561031c57604051639996b31560e01b81526001600160a01b03851660048201526024016100b5565b50805b9392505050565b8051156103365780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b634e487b7160e01b5f52604160045260245ffd5b5f5f60408385031215610374575f5ffd5b82356001600160a01b038116811461038a575f5ffd5b9150602083013567ffffffffffffffff8111156103a5575f5ffd5b8301601f810185136103b5575f5ffd5b803567ffffffffffffffff8111156103cf576103cf61034f565b604051601f8201601f19908116603f0116810167ffffffffffffffff811182821017156103fe576103fe61034f565b604052818152828201602001871015610415575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b5f82518060208501845e5f92019182525091905056fea2646970667358221220117e3d359e3a2bb948f5bdb70d5aeec72a9db4978309bed659036d34fc5d4f6c64736f6c634300081b0033';
913

1014

1115
export const multicallAbi = parseAbi([

hyperdrive/packages/app-store/ui/src/pages/AppPage.tsx

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import useAppsStore from "../store/appStoreStore";
44
import { AppListing, PackageState, ManifestResponse, HomepageApp } from "../types/Apps";
55
import { compareVersions } from "../utils/compareVersions";
66
import { MirrorSelector, ManifestDisplay } from '../components';
7-
import { FaChevronDown, FaChevronRight, FaCheck, FaCircleNotch, FaPlay } from "react-icons/fa6";
7+
import { FaChevronDown, FaChevronRight, FaCheck, FaCircleNotch, FaPlay, FaXmark } from "react-icons/fa6";
88
import { BsDownload } from "react-icons/bs";
99
import { Modal } from "../components/Modal";
1010
import classNames from "classnames";
@@ -84,6 +84,8 @@ export default function AppPage() {
8484
const [hasProcessedIntent, setHasProcessedIntent] = useState(false);
8585
const [awaitPresenceInHomepageApps, setAwaitPresenceInHomepageApps] = useState(false);
8686
const [launchUrl, setLaunchUrl] = useState<string | null>(null);
87+
const [showScreenshots, setShowScreenshots] = useState(true);
88+
const [activeScreenshot, setActiveScreenshot] = useState<string | null>(null);
8789

8890
useEffect(() => {
8991
const backTickCounter = (e: KeyboardEvent) => {
@@ -111,6 +113,15 @@ export default function AppPage() {
111113
.sort((a, b) => compareVersions(b.version, a.version));
112114
}, [app]);
113115

116+
const screenshots = useMemo(() => {
117+
if (isDevMode) {
118+
return MOCK_APP.metadata?.properties?.screenshots ?? [];
119+
}
120+
return app?.metadata?.properties?.screenshots ?? [];
121+
}, [app, isDevMode]);
122+
123+
const hasScreenshots = screenshots.length > 0;
124+
114125
const isDownloaded = useMemo(() => {
115126
if (!app || !selectedVersion) return false;
116127
const versionData = sortedVersions.find(v => v.version === selectedVersion);
@@ -664,18 +675,53 @@ export default function AppPage() {
664675

665676
{appButtons({ className: "flex-col items-stretch md:hidden" })}
666677

667-
{(app.metadata?.properties?.screenshots || isDevMode) && (
668-
<div className="flex flex-col gap-2">
669-
<h3 className="prose">Screenshots</h3>
670-
<div className="flex flex-wrap gap-2 overflow-y-auto min-h-0 max-h-lg">
671-
{(isDevMode ? MOCK_APP.metadata!.properties.screenshots! : app!.metadata!.properties!.screenshots!).map((screenshot, index) => (
672-
<img
673-
src={screenshot}
674-
alt={`Screenshot ${index + 1}`}
675-
className="rounded-lg w-full h-full object-contain aspect-video max-w-md max-h-md"
676-
loading="lazy"
677-
/>
678-
))}
678+
{(hasScreenshots || isDevMode) && (
679+
<div className="flex flex-col gap-4">
680+
<button
681+
onClick={() => setShowScreenshots(!showScreenshots)}
682+
className="flex items-center gap-2 text-lg font-medium text-gray-900 dark:text-white hover:text-iris dark:hover:text-neon transition-colors"
683+
>
684+
{showScreenshots ? <FaChevronDown /> : <FaChevronRight />} Screenshots
685+
</button>
686+
{showScreenshots && (
687+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
688+
{screenshots.map((screenshot, index) => (
689+
<img
690+
key={index}
691+
src={screenshot}
692+
alt={`Screenshot ${index + 1}`}
693+
className="rounded-lg w-full h-auto object-cover aspect-video max-h-64 hover:scale-105 transition-transform cursor-pointer"
694+
loading="lazy"
695+
onClick={() => setActiveScreenshot(screenshot)}
696+
/>
697+
))}
698+
</div>
699+
)}
700+
</div>
701+
)}
702+
703+
{activeScreenshot && (
704+
<div
705+
className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-4"
706+
onClick={() => setActiveScreenshot(null)}
707+
>
708+
<div
709+
className="relative max-w-5xl max-h-[90vh] flex items-center justify-center"
710+
onClick={event => event.stopPropagation()}
711+
>
712+
<button
713+
type="button"
714+
className="absolute top-2 right-2 bg-black/60 text-white rounded-full p-2 hover:bg-black/80 transition-colors"
715+
onClick={() => setActiveScreenshot(null)}
716+
aria-label="Close screenshot preview"
717+
>
718+
<FaXmark size={18} />
719+
</button>
720+
<img
721+
src={activeScreenshot}
722+
alt="Selected app screenshot"
723+
className="max-h-[85vh] w-auto max-w-full rounded-lg shadow-2xl"
724+
/>
679725
</div>
680726
</div>
681727
)}

hyperdrive/packages/app-store/ui/src/pages/PublishPage.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { ConnectButton, useConnectModal } from '@rainbow-me/rainbowkit';
55
import { keccak256, toBytes } from 'viem';
66
import { mechAbi, HYPERMAP, encodeIntoMintCall, encodeMulticalls, hypermapAbi, MULTICALL } from "../abis";
77
import { hyperhash } from '../utils/hyperhash';
8+
import { predictTBAAddress } from '../utils/predictTBA';
89
import useAppsStore from "../store/appStoreStore";
910
import { PackageSelector } from "../components";
1011
import { Tooltip } from '../components/Tooltip';
@@ -229,7 +230,9 @@ export default function PublishPage() {
229230
metadata = keccak256(toBytes(metadataText));
230231
}
231232

232-
const multicall = encodeMulticalls(metadataUrl, metadata);
233+
// When creating a new package, predict the TBA address that will be created
234+
const predictedTBA = !isUpdate ? predictTBAAddress(currentTBA || HYPERMAP, packageName, publicClient?.chain?.id || 8453) : undefined;
235+
const multicall = encodeMulticalls(metadataUrl, metadata, predictedTBA?.predictedAddress);
233236
const args = isUpdate ? multicall : encodeIntoMintCall(multicall, address, packageName);
234237

235238
writeContract({
@@ -444,4 +447,4 @@ export default function PublishPage() {
444447
)}
445448
</div>
446449
);
447-
}
450+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { encodePacked, keccak256, getAddress, encodeAbiParameters, type Address, type Hex } from 'viem';
2+
import { hyperhash } from './hyperhash';
3+
import { PROXY_CREATION_CODE, ERC6551_REGISTRY } from "../abis";
4+
5+
export function predictTBAAddress(
6+
hypermapAddr: Address,
7+
label: string,
8+
chainId: number = 8453 // Base chain ID
9+
): { predictedAddress: Address, predictedTokenId: BigInt } {
10+
// Calculate the namehash for the label
11+
const namehash = hyperhash(label);
12+
// First compute the proxy address
13+
const proxyAddr = computeProxyAddress(hypermapAddr, hypermapAddr, namehash);
14+
console.log("proxyAddr", proxyAddr);
15+
const predictedTokenId = BigInt(namehash)
16+
const predictedAddress = computeAccount(proxyAddr, namehash, BigInt(chainId), hypermapAddr, predictedTokenId);
17+
return { predictedAddress, predictedTokenId };
18+
}
19+
20+
function computeAccount(
21+
implementation: Address,
22+
salt: bigint | `0x${string}`,
23+
chainId: bigint,
24+
tokenContract: Address,
25+
tokenId: bigint
26+
): Address {
27+
// ERC-1167 minimal proxy bytecode components
28+
const fullHeader = "0x3d60ad80600a3d3981f3363d3d373d3d3d363d73" as Hex;
29+
const footer = "0x5af43d82803e903d91602b57fd5bf3" as Hex;
30+
31+
const bytecode = encodePacked(
32+
["bytes", "address", "bytes"],
33+
[fullHeader, implementation, footer]
34+
);
35+
36+
// Encode the constructor arguments (salt, chainId, tokenContract, tokenId)
37+
const constructorArgs = encodeAbiParameters(
38+
[
39+
{ type: "bytes32" },
40+
{ type: "uint256" },
41+
{ type: "address" },
42+
{ type: "uint256" },
43+
],
44+
[salt as `0x${string}`, chainId, tokenContract, tokenId]
45+
);
46+
47+
// Combine bytecode with constructor arguments to match the exact memory layout
48+
const initCode = encodePacked(
49+
["bytes", "bytes"],
50+
[bytecode, constructorArgs]
51+
);
52+
53+
// CREATE2 formula
54+
const create2Hash = keccak256(
55+
encodePacked(
56+
["bytes1", "address", "bytes32", "bytes32"],
57+
["0xff" as Hex, ERC6551_REGISTRY, salt as `0x${string}`, keccak256(initCode)]
58+
)
59+
);
60+
61+
return getAddress(`0x${create2Hash.slice(-40)}`);
62+
63+
}
64+
65+
66+
function computeProxyAddress(
67+
deployer: Address,
68+
hypermapAddr: Address,
69+
salt: string
70+
): Address {
71+
72+
const proxyCreationCodeHash = keccak256(
73+
encodePacked(
74+
['bytes', 'bytes'],
75+
[
76+
PROXY_CREATION_CODE,
77+
encodeAbiParameters(
78+
[{ type: 'address' }],
79+
[getAddress(hypermapAddr)]
80+
)
81+
]
82+
)
83+
);
84+
85+
const hash = keccak256(
86+
encodePacked(
87+
['bytes1', 'address', 'bytes32', 'bytes32'],
88+
['0xff', deployer, salt as `0x${string}`, proxyCreationCodeHash]
89+
)
90+
);
91+
return getAddress(`0x${hash.slice(-40)}`) as Address;
92+
}

hyperdrive/packages/file-explorer/explorer/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ const ICON: &str = include_str!("./icon");
1111
const PROCESS_ID_LINK: &str = "explorer:file-explorer:sys";
1212

1313
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
14-
#[serde(rename_all = "camelCase")]
1514
pub struct FileInfo {
1615
pub name: String,
1716
pub path: String,

0 commit comments

Comments
 (0)