Skip to content

Commit 137db6a

Browse files
authored
Merge branch 'main' into Joe---AA-Supported-Chains
2 parents c904a25 + 1e8d1e4 commit 137db6a

File tree

10 files changed

+323
-19
lines changed

10 files changed

+323
-19
lines changed

.changeset/fresh-gorillas-sip.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Compute addresses with ref contracts

apps/dashboard/src/app/login/LoginPage.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
} from "thirdweb/react";
2020
import { createWallet, inAppWallet } from "thirdweb/wallets";
2121
import { ClientOnly } from "../../components/ClientOnly/ClientOnly";
22+
import { isVercel } from "../../lib/vercel-utils";
2223
import { ThirdwebMiniLogo } from "../components/ThirdwebMiniLogo";
2324
import { getSDKTheme } from "../components/sdk-component-theme";
2425
import { doLogin, doLogout, getLoginPayload, isLoggedIn } from "./auth-actions";
@@ -246,14 +247,17 @@ function CustomConnectEmbed(props: {
246247
}) {
247248
const { theme } = useTheme();
248249
const client = useThirdwebClient();
249-
const [turnstileToken, setTurnstileToken] = useState("");
250+
const [turnstileToken, setTurnstileToken] = useState<string | undefined>(
251+
undefined,
252+
);
253+
const [alwaysShowTurnstile, setAlwaysShowTurnstile] = useState(false);
250254

251255
return (
252256
<div className="flex flex-col items-center gap-4">
253257
<Turnstile
254258
options={{
255259
// only show if interaction is required
256-
appearance: "interaction-only",
260+
appearance: alwaysShowTurnstile ? "always" : "interaction-only",
257261
// match the theme of the rest of the app
258262
theme: theme === "light" ? "light" : "dark",
259263
}}
@@ -265,6 +269,11 @@ function CustomConnectEmbed(props: {
265269
auth={{
266270
getLoginPayload,
267271
doLogin: async (params) => {
272+
if (isVercel() && !turnstileToken) {
273+
setAlwaysShowTurnstile(true);
274+
throw new Error("Please complete the captcha.");
275+
}
276+
268277
try {
269278
const result = await doLogin(params, turnstileToken);
270279
if (result.error) {

apps/dashboard/src/app/login/auth-actions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export async function getLoginPayload(
4040

4141
export async function doLogin(
4242
payload: VerifyLoginPayloadParams,
43-
turnstileToken: string,
43+
turnstileToken: string | undefined,
4444
) {
4545
if (!THIRDWEB_API_SECRET) {
4646
throw new Error("API_SERVER_SECRET is not set");

apps/dashboard/src/app/nebula-app/(app)/components/ChatPageContent.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ export function ChatPageContent(props: {
9696
const setContextFilters = useCallback((v: NebulaContext | undefined) => {
9797
_setContextFilters(v);
9898
setHasUserUpdatedContextFilters(true);
99+
saveLastUsedChainIds(v?.chainIds || undefined);
99100
}, []);
100101

101102
const isNewSession = !props.session;
@@ -118,7 +119,21 @@ export function ChatPageContent(props: {
118119
walletAddress: null,
119120
};
120121

122+
// Only set wallet address from connected wallet
121123
updatedContextFilters.walletAddress = address || null;
124+
125+
// if we have last used chains in storage, continue using them
126+
try {
127+
const lastUsedChainIds = getLastUsedChainIds();
128+
if (lastUsedChainIds) {
129+
updatedContextFilters.chainIds = lastUsedChainIds;
130+
return updatedContextFilters;
131+
}
132+
} catch {
133+
// ignore local storage errors
134+
}
135+
136+
// else - use the active chain
122137
updatedContextFilters.chainIds = activeChain
123138
? [activeChain.id.toString()]
124139
: [];
@@ -493,3 +508,31 @@ function WalletDisconnectedDialog(props: {
493508
</Dialog>
494509
);
495510
}
511+
512+
const NEBULA_LAST_USED_CHAIN_IDS_KEY = "nebula-last-used-chain-ids";
513+
514+
function saveLastUsedChainIds(chainIds: string[] | undefined) {
515+
try {
516+
if (chainIds && chainIds.length > 0) {
517+
localStorage.setItem(
518+
NEBULA_LAST_USED_CHAIN_IDS_KEY,
519+
JSON.stringify(chainIds),
520+
);
521+
} else {
522+
localStorage.removeItem(NEBULA_LAST_USED_CHAIN_IDS_KEY);
523+
}
524+
} catch {
525+
// ignore local storage errors
526+
}
527+
}
528+
529+
function getLastUsedChainIds(): string[] | null {
530+
try {
531+
const lastUsedChainIdsStr = localStorage.getItem(
532+
NEBULA_LAST_USED_CHAIN_IDS_KEY,
533+
);
534+
return lastUsedChainIdsStr ? JSON.parse(lastUsedChainIdsStr) : null;
535+
} catch {
536+
return null;
537+
}
538+
}

apps/portal/src/components/Document/Code.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export async function CodeBlock(props: {
6868
<div className={cn("group/code relative mb-5", props.containerClassName)}>
6969
<code
7070
className={cn(
71-
"relative block rounded-lg border bg-card font-mono text-sm leading-relaxed",
71+
"relative block whitespace-pre rounded-lg border bg-card font-mono text-sm leading-relaxed",
7272
props.className,
7373
)}
7474
lang={lang}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import type { Chain } from "../../chains/types.js";
2+
import type { ThirdwebClient } from "../../client/client.js";
3+
import { encodeAbiParameters } from "../../utils/abi/encodeAbiParameters.js";
4+
import { computePublishedContractAddress } from "../../utils/any-evm/compute-published-contract-address.js";
5+
import type { ImplementationConstructorParam } from "./process-ref-deployments.js";
6+
7+
type ComputeRefDeploymentsOptions = {
8+
client: ThirdwebClient;
9+
chain: Chain;
10+
paramValue: string | ImplementationConstructorParam;
11+
};
12+
13+
/**
14+
* Computes addresses for published contract references in constructor params.
15+
* @returns Param value after processing references.
16+
* @internal
17+
*/
18+
export async function computeRefDeployments(
19+
options: ComputeRefDeploymentsOptions,
20+
): Promise<string | string[]> {
21+
const { client, chain, paramValue } = options;
22+
23+
if (typeof paramValue === "object") {
24+
if (
25+
"defaultValue" in paramValue &&
26+
paramValue.defaultValue &&
27+
paramValue.defaultValue.length > 0
28+
) {
29+
return paramValue.defaultValue;
30+
}
31+
32+
if ("dynamicValue" in paramValue && paramValue.dynamicValue) {
33+
const dynamicValue = paramValue.dynamicValue;
34+
const contracts = dynamicValue.refContracts;
35+
36+
if (dynamicValue.type === "address") {
37+
if (!contracts || contracts.length === 0 || !contracts[0]?.contractId) {
38+
throw new Error("Invalid or empty param value");
39+
}
40+
const salt =
41+
contracts[0]?.salt && contracts[0]?.salt.length > 0
42+
? contracts[0]?.salt
43+
: "";
44+
45+
const addr = await computePublishedContractAddress({
46+
client,
47+
chain,
48+
contractId: contracts[0]?.contractId,
49+
publisher: contracts[0]?.publisherAddress,
50+
version: contracts[0]?.version,
51+
salt,
52+
});
53+
54+
return addr;
55+
}
56+
57+
if (dynamicValue.type === "address[]") {
58+
if (!contracts || contracts.length === 0) {
59+
throw new Error("Invalid or empty param value");
60+
}
61+
const addressArray: string[] = [];
62+
63+
for (const c of contracts) {
64+
const salt = c?.salt && c?.salt.length > 0 ? c?.salt : "";
65+
66+
addressArray.push(
67+
await computePublishedContractAddress({
68+
client,
69+
chain,
70+
contractId: c.contractId,
71+
publisher: c.publisherAddress,
72+
version: c.version,
73+
salt,
74+
}),
75+
);
76+
}
77+
78+
return addressArray;
79+
}
80+
81+
if (dynamicValue.type === "bytes") {
82+
if (!dynamicValue.paramsToEncode) {
83+
throw new Error("Invalid or empty param value");
84+
}
85+
const paramsToEncode = dynamicValue.paramsToEncode[0];
86+
87+
if (paramsToEncode) {
88+
const types: string[] = [];
89+
const values: (string | string[])[] = [];
90+
for (const v of paramsToEncode) {
91+
types.push(v.type);
92+
93+
if (v.defaultValue) {
94+
values.push(v.defaultValue);
95+
} else if (v.dynamicValue) {
96+
values.push(
97+
await computeRefDeployments({
98+
client,
99+
chain,
100+
paramValue: v,
101+
}),
102+
);
103+
}
104+
}
105+
106+
return encodeAbiParameters(
107+
types.map((t) => {
108+
return { type: t };
109+
}),
110+
values,
111+
);
112+
}
113+
}
114+
115+
if (dynamicValue.type === "bytes[]") {
116+
if (!dynamicValue.paramsToEncode) {
117+
throw new Error("Invalid or empty param value");
118+
}
119+
const bytesArray: string[] = [];
120+
const paramArray = dynamicValue.paramsToEncode;
121+
122+
for (const a of paramArray) {
123+
const paramsToEncode = a;
124+
125+
if (paramsToEncode) {
126+
const types: string[] = [];
127+
const values: (string | string[])[] = [];
128+
for (const v of paramsToEncode) {
129+
types.push(v.type);
130+
131+
if (v.defaultValue) {
132+
values.push(v.defaultValue);
133+
} else if (v.dynamicValue) {
134+
values.push(
135+
await computeRefDeployments({
136+
client,
137+
chain,
138+
paramValue: v,
139+
}),
140+
);
141+
}
142+
}
143+
144+
bytesArray.push(
145+
encodeAbiParameters(
146+
types.map((t) => {
147+
return { type: t };
148+
}),
149+
values,
150+
),
151+
);
152+
}
153+
}
154+
155+
return bytesArray;
156+
}
157+
}
158+
}
159+
160+
return paramValue as string;
161+
}

0 commit comments

Comments
 (0)