Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions hyperdrive/packages/app-store/ui/src/abis/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { multicallAbi, hypermapAbi, mechAbi, HYPERMAP, MULTICALL, HYPER_ACCOUNT_UPGRADABLE_IMPL } from "./";
import { encodeFunctionData, encodePacked, stringToHex } from "viem";

export function encodeMulticalls(metadataUri: string, metadataHash: string) {
export function encodeMulticalls(metadataUri: string, metadataHash: string, tbaAddress?: `0x${string}`) {
const metadataHashCall = encodeFunctionData({
abi: hypermapAbi,
functionName: 'note',
Expand All @@ -20,11 +20,22 @@ export function encodeMulticalls(metadataUri: string, metadataHash: string) {
]
})

const calls = [
// Add initialize call if TBA address is provided
const initializeCall = tbaAddress ? encodeFunctionData({
abi: [{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"}],
functionName: 'initialize',
args: []
}) : null;

const baseCalls = [
{ target: HYPERMAP, callData: metadataHashCall },
{ target: HYPERMAP, callData: metadataUriCall },
];

const calls = initializeCall && tbaAddress ?
[{ target: tbaAddress, callData: initializeCall }, ...baseCalls] :
baseCalls;

const multicall = encodeFunctionData({
abi: multicallAbi,
functionName: 'aggregate',
Expand Down
7 changes: 5 additions & 2 deletions hyperdrive/packages/app-store/ui/src/pages/PublishPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ConnectButton, useConnectModal } from '@rainbow-me/rainbowkit';
import { keccak256, toBytes } from 'viem';
import { mechAbi, HYPERMAP, encodeIntoMintCall, encodeMulticalls, hypermapAbi, MULTICALL } from "../abis";
import { hyperhash } from '../utils/hyperhash';
import { predictTBAAddress } from '../utils/predictTBA';
import useAppsStore from "../store/appStoreStore";
import { PackageSelector } from "../components";
import { Tooltip } from '../components/Tooltip';
Expand Down Expand Up @@ -229,7 +230,9 @@ export default function PublishPage() {
metadata = keccak256(toBytes(metadataText));
}

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

writeContract({
Expand Down Expand Up @@ -444,4 +447,4 @@ export default function PublishPage() {
)}
</div>
);
}
}
94 changes: 94 additions & 0 deletions hyperdrive/packages/app-store/ui/src/utils/predictTBA.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { encodePacked, keccak256, getAddress, encodeAbiParameters, type Address, type Hex } from 'viem';
import { hyperhash } from './hyperhash';

const ERC6551_REGISTRY = '0x000000006551c19487814612e58FE06813775758' as const;

export function predictTBAAddress(
hypermapAddr: Address,
label: string,
chainId: number = 8453 // Base chain ID
): Address {
// Calculate the namehash for the label
const namehash = hyperhash(label);

// First compute the proxy address
const proxyAddr = computeProxyAddress(hypermapAddr, hypermapAddr, namehash);
console.log("proxyAddr", proxyAddr);
return computeAccount(proxyAddr, namehash, BigInt(chainId), hypermapAddr, BigInt(namehash));
}

function computeAccount(
implementation: Address,
salt: bigint | `0x${string}`,
chainId: bigint,
tokenContract: Address,
tokenId: bigint
): Address {
// ERC-1167 minimal proxy bytecode components
const fullHeader = "0x3d60ad80600a3d3981f3363d3d373d3d3d363d73" as Hex;
const footer = "0x5af43d82803e903d91602b57fd5bf3" as Hex;

const bytecode = encodePacked(
["bytes", "address", "bytes"],
[fullHeader, implementation, footer]
);

// Encode the constructor arguments (salt, chainId, tokenContract, tokenId)
const constructorArgs = encodeAbiParameters(
[
{ type: "bytes32" },
{ type: "uint256" },
{ type: "address" },
{ type: "uint256" },
],
[salt as `0x${string}`, chainId, tokenContract, tokenId]
);

// Combine bytecode with constructor arguments to match the exact memory layout
const initCode = encodePacked(
["bytes", "bytes"],
[bytecode, constructorArgs]
);

// CREATE2 formula
const create2Hash = keccak256(
encodePacked(
["bytes1", "address", "bytes32", "bytes32"],
["0xff" as Hex, ERC6551_REGISTRY, salt as `0x${string}`, keccak256(initCode)]
)
);

return getAddress(`0x${create2Hash.slice(-40)}`);

}


function computeProxyAddress(
deployer: Address,
hypermapAddr: Address,
salt: string
): Address {
// HyperAccountProxy creation code with constructor argument
const PROXY_CREATION_CODE = '0x60a0604052348015600e575f5ffd5b5060405161051d38038061051d833981016040819052602b91603b565b6001600160a01b03166080526066565b5f60208284031215604a575f5ffd5b81516001600160a01b0381168114605f575f5ffd5b9392505050565b6080516104a061007d5f395f607a01526104a05ff3fe608060405260043610610021575f3560e01c8063d1f578941461003257610028565b3661002857005b610030610045565b005b610030610040366004610383565b610057565b610055610050610132565b610169565b565b7f7d0893b5fe6077fb4cf083ec3487b8eece7e03b4ab6e888f7a8a1758010f8c007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633146100df57805460ff16156100bf576100ba610045565b6100df565b60405163572190d160e01b81523360048201526024015b60405180910390fd5b805460ff16156101015760405162dc149f60e41b815260040160405180910390fd5b5f61010a610132565b6001600160a01b03160361012d57805460ff1916600117815561012d8383610187565b505050565b5f6101647f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b365f5f375f5f365f845af43d5f5f3e808015610183573d5ff35b3d5ffd5b610190826101e0565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156101d45761012d8282610256565b6101dc6102c8565b5050565b806001600160a01b03163b5f0361021557604051634c9c8ce360e01b81526001600160a01b03821660048201526024016100d6565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f5f846001600160a01b0316846040516102729190610454565b5f60405180830381855af49150503d805f81146102aa576040519150601f19603f3d011682016040523d82523d5f602084013e6102af565b606091505b50915091506102bf8583836102e7565b95945050505050565b34156100555760405163b398979f60e01b815260040160405180910390fd5b6060826102fc576102f782610346565b61033f565b815115801561031357506001600160a01b0384163b155b1561033c57604051639996b31560e01b81526001600160a01b03851660048201526024016100d6565b50805b9392505050565b8051156103565780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b634e487b7160e01b5f52604160045260245ffd5b5f5f60408385031215610394575f5ffd5b82356001600160a01b03811681146103aa575f5ffd5b9150602083013567ffffffffffffffff8111156103c5575f5ffd5b8301601f810185136103d5575f5ffd5b803567ffffffffffffffff8111156103ef576103ef61036f565b604051601f8201601f19908116603f0116810167ffffffffffffffff8111828210171561041e5761041e61036f565b604052818152828201602001871015610435575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b5f82518060208501845e5f92019182525091905056fea26469706673582212205c8437c90a52b26afb62a6e21b8baa0d106dcc547054521f0074dea229fd630f64736f6c634300081c0033';

const proxyCreationCodeHash = keccak256(
encodePacked(
['bytes', 'bytes'],
[
PROXY_CREATION_CODE,
encodeAbiParameters(
[{ type: 'address' }],
[getAddress(hypermapAddr)]
)
]
)
);

const hash = keccak256(
encodePacked(
['bytes1', 'address', 'bytes32', 'bytes32'],
['0xff', deployer, salt as `0x${string}`, proxyCreationCodeHash]
)
);
return getAddress(`0x${hash.slice(-40)}`) as Address;
}
15 changes: 14 additions & 1 deletion hyperdrive/src/register-ui/src/abis/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const generateNetworkingKeys = async ({
setTcpPort,
setRouters,
reset,
tbaAddress,
}: {
direct: boolean,
label: string,
Expand All @@ -29,6 +30,7 @@ export const generateNetworkingKeys = async ({
setTcpPort: (tcpPort: number) => void;
setRouters: (routers: string[]) => void;
reset: boolean;
tbaAddress?: `0x${string}`;
}) => {
const {
networking_key,
Expand Down Expand Up @@ -106,7 +108,14 @@ export const generateNetworkingKeys = async ({
)]
});

const calls = direct ? [
// Add initialize call if TBA address is provided
const initializeCall = tbaAddress ? encodeFunctionData({
abi: [{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"}],
functionName: 'initialize',
args: []
}) : null;

const baseCalls = direct ? [
{ target: HYPERMAP, callData: netkeycall },
{ target: HYPERMAP, callData: ws_port_call },
{ target: HYPERMAP, callData: tcp_port_call },
Expand All @@ -116,6 +125,10 @@ export const generateNetworkingKeys = async ({
{ target: HYPERMAP, callData: router_call },
];

const calls = initializeCall && tbaAddress ?
[{ target: tbaAddress, callData: initializeCall }, ...baseCalls] :
baseCalls;

const multicalls = encodeFunctionData({
abi: multicallAbi,
functionName: 'aggregate',
Expand Down
14 changes: 11 additions & 3 deletions hyperdrive/src/register-ui/src/pages/MintCustom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import DirectNodeCheckbox from "../components/DirectCheckbox";

import { useAccount, useWaitForTransactionReceipt, useSendTransaction } from "wagmi";
import { useConnectModal, useAddRecentTransaction } from "@rainbow-me/rainbowkit"
import { tbaMintAbi, generateNetworkingKeys, HYPER_ACCOUNT_IMPL } from "../abis";
import { tbaMintAbi, generateNetworkingKeys, HYPER_ACCOUNT_IMPL, HYPERMAP } from "../abis";
import { encodePacked, encodeFunctionData, stringToHex } from "viem";
import BackButton from "../components/BackButton";
import { predictTBAAddress } from "../utils/predictTBA";
interface MintCustomNameProps extends PageProps { }

function MintCustom({
Expand Down Expand Up @@ -65,6 +66,13 @@ function MintCustom({
return
}

const name = formData.get('name') as string
const tbaAddr = formData.get('tba') as `0x${string}` || HYPERMAP;
const fullLabel = `${name}.${tbaAddr === HYPERMAP ? '' : tbaAddr}`;

// Predict the TBA address that will be created
const predictedTBA = predictTBAAddress(tbaAddr, name);

const initCall = await generateNetworkingKeys({
direct,
our_address: address,
Expand All @@ -75,14 +83,14 @@ function MintCustom({
setTcpPort,
setRouters,
reset: false,
tbaAddress: predictedTBA,
});

setHnsName(formData.get('full-hns-name') as string)

const name = formData.get('name') as string

console.log("full hns name", formData.get('full-hns-name'))
console.log("name", name)
console.log("predicted TBA", predictedTBA)

const data = encodeFunctionData({
abi: tbaMintAbi,
Expand Down
11 changes: 8 additions & 3 deletions hyperdrive/src/register-ui/src/pages/MintDotOsName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useConnectModal, useAddRecentTransaction } from "@rainbow-me/rainbowkit
import { generateNetworkingKeys, HYPER_ACCOUNT_IMPL, DOTOS, tbaMintAbi } from "../abis";
import { createPublicClient, encodePacked, http, stringToHex, BaseError, ContractFunctionRevertedError } from "viem";
import { base } from 'viem/chains'
import { predictTBAAddress } from "../utils/predictTBA";

interface RegisterOsNameProps extends PageProps { }

Expand Down Expand Up @@ -60,6 +61,12 @@ function MintDotOsName({

setHasMinted(true);

// strip .os suffix
const name = hnsName.replace(/\.os$/, '');

// Predict the TBA address that will be created
const predictedTBA = predictTBAAddress(DOTOS, name, base.id);

const initCall = await generateNetworkingKeys({
direct,
our_address: address,
Expand All @@ -70,11 +77,9 @@ function MintDotOsName({
setTcpPort,
setRouters,
reset: false,
tbaAddress: predictedTBA,
});

// strip .os suffix
const name = hnsName.replace(/\.os$/, '');

const publicClient = createPublicClient({
chain: base,
transport: http(),
Expand Down
94 changes: 94 additions & 0 deletions hyperdrive/src/register-ui/src/utils/predictTBA.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { encodePacked, keccak256, getAddress, encodeAbiParameters, type Address, type Hex } from 'viem';
import { hyperhash } from './hyperhash';

const ERC6551_REGISTRY = '0x000000006551c19487814612e58FE06813775758' as const;

export function predictTBAAddress(
hypermapAddr: Address,
label: string,
chainId: number = 8453 // Base chain ID
): Address {
// Calculate the namehash for the label
const namehash = hyperhash(label);

// First compute the proxy address
const proxyAddr = computeProxyAddress(hypermapAddr, hypermapAddr, namehash);
console.log("proxyAddr", proxyAddr);
return computeAccount(proxyAddr, namehash, BigInt(chainId), hypermapAddr, BigInt(namehash));
}

function computeAccount(
implementation: Address,
salt: bigint | `0x${string}`,
chainId: bigint,
tokenContract: Address,
tokenId: bigint
): Address {
// ERC-1167 minimal proxy bytecode components
const fullHeader = "0x3d60ad80600a3d3981f3363d3d373d3d3d363d73" as Hex;
const footer = "0x5af43d82803e903d91602b57fd5bf3" as Hex;

const bytecode = encodePacked(
["bytes", "address", "bytes"],
[fullHeader, implementation, footer]
);

// Encode the constructor arguments (salt, chainId, tokenContract, tokenId)
const constructorArgs = encodeAbiParameters(
[
{ type: "bytes32" },
{ type: "uint256" },
{ type: "address" },
{ type: "uint256" },
],
[salt as `0x${string}`, chainId, tokenContract, tokenId]
);

// Combine bytecode with constructor arguments to match the exact memory layout
const initCode = encodePacked(
["bytes", "bytes"],
[bytecode, constructorArgs]
);

// CREATE2 formula
const create2Hash = keccak256(
encodePacked(
["bytes1", "address", "bytes32", "bytes32"],
["0xff" as Hex, ERC6551_REGISTRY, salt as `0x${string}`, keccak256(initCode)]
)
);

return getAddress(`0x${create2Hash.slice(-40)}`);

}


function computeProxyAddress(
deployer: Address,
hypermapAddr: Address,
salt: string
): Address {
// HyperAccountProxy creation code with constructor argument
const PROXY_CREATION_CODE = '0x60a0604052348015600e575f5ffd5b5060405161051d38038061051d833981016040819052602b91603b565b6001600160a01b03166080526066565b5f60208284031215604a575f5ffd5b81516001600160a01b0381168114605f575f5ffd5b9392505050565b6080516104a061007d5f395f607a01526104a05ff3fe608060405260043610610021575f3560e01c8063d1f578941461003257610028565b3661002857005b610030610045565b005b610030610040366004610383565b610057565b610055610050610132565b610169565b565b7f7d0893b5fe6077fb4cf083ec3487b8eece7e03b4ab6e888f7a8a1758010f8c007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633146100df57805460ff16156100bf576100ba610045565b6100df565b60405163572190d160e01b81523360048201526024015b60405180910390fd5b805460ff16156101015760405162dc149f60e41b815260040160405180910390fd5b5f61010a610132565b6001600160a01b03160361012d57805460ff1916600117815561012d8383610187565b505050565b5f6101647f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b365f5f375f5f365f845af43d5f5f3e808015610183573d5ff35b3d5ffd5b610190826101e0565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156101d45761012d8282610256565b6101dc6102c8565b5050565b806001600160a01b03163b5f0361021557604051634c9c8ce360e01b81526001600160a01b03821660048201526024016100d6565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f5f846001600160a01b0316846040516102729190610454565b5f60405180830381855af49150503d805f81146102aa576040519150601f19603f3d011682016040523d82523d5f602084013e6102af565b606091505b50915091506102bf8583836102e7565b95945050505050565b34156100555760405163b398979f60e01b815260040160405180910390fd5b6060826102fc576102f782610346565b61033f565b815115801561031357506001600160a01b0384163b155b1561033c57604051639996b31560e01b81526001600160a01b03851660048201526024016100d6565b50805b9392505050565b8051156103565780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b634e487b7160e01b5f52604160045260245ffd5b5f5f60408385031215610394575f5ffd5b82356001600160a01b03811681146103aa575f5ffd5b9150602083013567ffffffffffffffff8111156103c5575f5ffd5b8301601f810185136103d5575f5ffd5b803567ffffffffffffffff8111156103ef576103ef61036f565b604051601f8201601f19908116603f0116810167ffffffffffffffff8111828210171561041e5761041e61036f565b604052818152828201602001871015610435575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b5f82518060208501845e5f92019182525091905056fea26469706673582212205c8437c90a52b26afb62a6e21b8baa0d106dcc547054521f0074dea229fd630f64736f6c634300081c0033';

const proxyCreationCodeHash = keccak256(
encodePacked(
['bytes', 'bytes'],
[
PROXY_CREATION_CODE,
encodeAbiParameters(
[{ type: 'address' }],
[getAddress(hypermapAddr)]
)
]
)
);

const hash = keccak256(
encodePacked(
['bytes1', 'address', 'bytes32', 'bytes32'],
['0xff', deployer, salt as `0x${string}`, proxyCreationCodeHash]
)
);
return getAddress(`0x${hash.slice(-40)}`) as Address;
}