From 131261bdf7417708ec1f04600e094cbf48333b33 Mon Sep 17 00:00:00 2001 From: Iris Date: Mon, 22 Apr 2024 11:52:21 +0200 Subject: [PATCH 1/2] ref: update starknetid hooks for address_to_domain update --- packages/core/src/hooks/useStarkAddress.ts | 4 +- packages/core/src/hooks/useStarkName.ts | 86 ++++++- packages/core/src/hooks/useStarkProfile.ts | 255 +++++++++++++-------- 3 files changed, 233 insertions(+), 112 deletions(-) diff --git a/packages/core/src/hooks/useStarkAddress.ts b/packages/core/src/hooks/useStarkAddress.ts index 73aea6c2..150ae71e 100644 --- a/packages/core/src/hooks/useStarkAddress.ts +++ b/packages/core/src/hooks/useStarkAddress.ts @@ -84,7 +84,7 @@ function queryFn({ const namingContract = contract ?? StarknetIdNamingContract[network]; const p = new Provider(provider); - const encodedDomain = decodeDomain(name); + const encodedDomain = encodeDomain(name); const result = await p.callContract({ contractAddress: namingContract as string, entrypoint: "domain_to_address", @@ -106,7 +106,7 @@ const StarknetIdNamingContract: Record = { mainnet: "0x6ac597f8116f886fa1c97a23fa4e08299975ecaf6b598873ca6792b9bbfb678", }; -const decodeDomain = (domain: string): string[] => { +const encodeDomain = (domain: string): string[] => { if (!domain) return ["0"]; const encoded = []; diff --git a/packages/core/src/hooks/useStarkName.ts b/packages/core/src/hooks/useStarkName.ts index c0173f1b..c9457fcc 100644 --- a/packages/core/src/hooks/useStarkName.ts +++ b/packages/core/src/hooks/useStarkName.ts @@ -1,5 +1,11 @@ import { useMemo } from "react"; -import { Provider, ProviderInterface } from "starknet"; +import { + Call, + CallData, + Provider, + ProviderInterface, + starknetId, +} from "starknet"; import { UseQueryProps, UseQueryResult, useQuery } from "~/query"; import { useProvider } from "./useProvider"; @@ -67,19 +73,15 @@ export function useStarkName({ }: StarkNameArgs): StarkNameResult { const { provider } = useProvider(); const { chain } = useNetwork(); - contract = - chain.network === "sepolia" - ? "0x0707f09bc576bd7cfee59694846291047e965f4184fe13dac62c56759b3b6fa7" - : contract; const enabled = useMemo( () => Boolean(enabled_ && address), - [enabled_, address], + [enabled_, address] ); return useQuery({ - queryKey: queryKey({ address, contract }), - queryFn: queryFn({ address, contract, provider }), + queryKey: queryKey({ address, contract, network: chain.network }), + queryFn: queryFn({ address, contract, provider, network: chain.network }), enabled, ...props, }); @@ -88,22 +90,84 @@ export function useStarkName({ function queryKey({ address, contract, + network, }: { address?: string; contract?: string; + network?: string; }) { - return [{ entity: "starkName", address, contract }] as const; + return [{ entity: "starkName", address, contract, network }] as const; } function queryFn({ address, contract, provider, -}: StarkNameArgs & { provider: ProviderInterface }) { + network, +}: StarkNameArgs & { provider: ProviderInterface } & { + network: string; +}) { return async function () { if (!address) throw new Error("address is required"); + const namingContract = contract ?? StarknetIdNamingContract[network]; const p = new Provider(provider); - return await p.getStarkName(address, contract); + try { + // remove fallback when starknetid naming contract is updated on mainnet + const calldata: Call = { + contractAddress: namingContract as string, + entrypoint: "address_to_domain", + calldata: CallData.compile({ + address: address, + hint: [], + }), + }; + const fallbackCalldata: Call = { + contractAddress: namingContract as string, + entrypoint: "address_to_domain", + calldata: CallData.compile({ + address: address, + }), + }; + const hexDomain = await executeWithFallback( + p, + calldata, + fallbackCalldata + ); + const decimalDomain = hexDomain.result + .map((element) => BigInt(element)) + .slice(1); + const stringDomain = starknetId.useDecoded(decimalDomain); + if (!stringDomain) { + throw new Error("Could not get stark name"); + } + return stringDomain; + } catch (e) { + throw new Error("Could not get stark name"); + } }; } + +const executeWithFallback = async ( + provider: ProviderInterface, + initialCall: Call, + fallbackCall: Call +) => { + try { + // Attempt the initial call with the hint parameter + return await provider.callContract(initialCall); + } catch (initialError) { + // If the initial call fails, try with the fallback calldata without the hint parameter + try { + return await provider.callContract(fallbackCall); + } catch (fallbackError) { + throw fallbackError; // Re-throw to handle outside + } + } +}; + +const StarknetIdNamingContract: Record = { + goerli: "0x3bab268e932d2cecd1946f100ae67ce3dff9fd234119ea2f6da57d16d29fce", + sepolia: "0x0707f09bc576bd7cfee59694846291047e965f4184fe13dac62c56759b3b6fa7", + mainnet: "0x6ac597f8116f886fa1c97a23fa4e08299975ecaf6b598873ca6792b9bbfb678", +}; diff --git a/packages/core/src/hooks/useStarkProfile.ts b/packages/core/src/hooks/useStarkProfile.ts index 0400c47a..9e0833f8 100644 --- a/packages/core/src/hooks/useStarkProfile.ts +++ b/packages/core/src/hooks/useStarkProfile.ts @@ -2,11 +2,12 @@ import { useMemo } from "react"; import { CairoCustomEnum, ContractInterface, - Provider, ProviderInterface, + RawArgsArray, cairo, hash, shortString, + starknetId, } from "starknet"; import { UseQueryProps, UseQueryResult, useQuery } from "~/query"; @@ -113,6 +114,8 @@ export function useStarkProfile({ }: StarkProfileArgs): useStarkProfileResult { const { provider } = useProvider(); const { chain } = useNetwork(); + if (!StarknetIdcontracts[chain.network]) + throw new Error("Network not supported"); const { contract: multicallContract } = useContract({ abi: multicallABI, address: (StarknetIdcontracts[chain.network] as any)["multicall"], @@ -120,7 +123,7 @@ export function useStarkProfile({ const enabled = useMemo( () => Boolean(enabled_ && address), - [enabled_, address], + [enabled_, address] ); return useQuery({ @@ -173,104 +176,24 @@ function queryFn({ const identity = identityContract ?? (contracts["identity"] as string); const naming = namingContract ?? (contracts["naming"] as string); - // get decoded starkname - const p = new Provider(provider); - const name = await p.getStarkName(address, naming); - - const data = await multicallContract.call("aggregate", [ - [ - { - execution: staticExecution(), - to: hardcoded(naming), - selector: hardcoded(hash.getSelectorFromName("address_to_domain")), - calldata: [hardcoded(address)], - }, - { - execution: staticExecution(), - to: hardcoded(naming), - selector: hardcoded(hash.getSelectorFromName("domain_to_id")), - calldata: [arrayReference(0, 0)], - }, - { - execution: staticExecution(), - to: hardcoded(identity), - selector: hardcoded(hash.getSelectorFromName("get_verifier_data")), - calldata: [ - reference(1, 0), - hardcoded(shortString.encodeShortString("twitter")), - hardcoded(contracts["verifier"] as string), - hardcoded("0"), - ], - }, - { - execution: staticExecution(), - to: hardcoded(identity), - selector: hardcoded(hash.getSelectorFromName("get_verifier_data")), - calldata: [ - reference(1, 0), - hardcoded(shortString.encodeShortString("github")), - hardcoded(contracts["verifier"] as string), - hardcoded("0"), - ], - }, - { - execution: staticExecution(), - to: hardcoded(identity), - selector: hardcoded(hash.getSelectorFromName("get_verifier_data")), - calldata: [ - reference(1, 0), - hardcoded(shortString.encodeShortString("discord")), - hardcoded(contracts["verifier"] as string), - hardcoded("0"), - ], - }, - { - execution: staticExecution(), - to: hardcoded(identity), - selector: hardcoded(hash.getSelectorFromName("get_verifier_data")), - calldata: [ - reference(1, 0), - hardcoded(shortString.encodeShortString("proof_of_personhood")), - hardcoded(contracts["verifier_pop"] as string), - hardcoded("0"), - ], - }, - // PFP - { - execution: staticExecution(), - to: hardcoded(identity), - selector: hardcoded(hash.getSelectorFromName("get_verifier_data")), - calldata: [ - reference(1, 0), - hardcoded(shortString.encodeShortString("nft_pp_contract")), - hardcoded(contracts["verifier_pfp"] as string), - hardcoded("0"), - ], - }, - { - execution: staticExecution(), - to: hardcoded(identity), - selector: hardcoded( - hash.getSelectorFromName("get_extended_verifier_data"), - ), - calldata: [ - reference(1, 0), - hardcoded(shortString.encodeShortString("nft_pp_id")), - hardcoded("2"), - hardcoded(contracts["verifier_pfp"] as string), - hardcoded("0"), - ], - }, - { - execution: notEqual(6, 0, 0), - to: reference(6, 0), - selector: hardcoded(hash.getSelectorFromName("tokenURI")), - calldata: [reference(7, 1), reference(7, 2)], - }, - ], - ]); + const { initialCalldata, fallbackCalldata } = getStarkProfileCalldata( + address, + naming, + identity, + contracts + ); + const data = await executeMulticallWithFallback( + multicallContract, + "aggregate", + initialCalldata, + fallbackCalldata + ); if (Array.isArray(data)) { + const name = + data[0][0] !== BigInt(0) + ? starknetId.useDecoded(data[0].slice(1)) + : undefined; const twitter = data[2][0] !== BigInt(0) ? data[2][0].toString() : undefined; const github = @@ -284,7 +207,7 @@ function queryFn({ ? data[8] .slice(1) .map((val: BigInt) => - shortString.decodeShortString(val.toString()), + shortString.decodeShortString(val.toString()) ) .join("") : undefined; @@ -406,6 +329,140 @@ const StarknetIdcontracts: Record> = { }, }; +const executeMulticallWithFallback = async ( + contract: ContractInterface, + functionName: string, + initialCalldata: RawArgsArray, + fallbackCalldata: RawArgsArray +) => { + try { + // Attempt the initial call with the new hint parameter + return await contract.call(functionName, [initialCalldata]); + } catch (initialError) { + // If the initial call fails, try with the fallback calldata without the hint parameter + try { + return await contract.call(functionName, [fallbackCalldata]); + } catch (fallbackError) { + throw fallbackError; // Re-throw to handle outside + } + } +}; + +const getStarkProfileCalldata = ( + address: string, + namingContract: string, + identityContract: string, + contracts: Record +): { + initialCalldata: RawArgsArray; + fallbackCalldata: RawArgsArray; +} => { + let initialCalldata: RawArgsArray = []; + let fallbackCalldata: RawArgsArray = []; + + initialCalldata.push({ + execution: staticExecution(), + to: hardcoded(namingContract), + selector: hardcoded(hash.getSelectorFromName("address_to_domain")), + calldata: [hardcoded(address), hardcoded("0")], + }); + fallbackCalldata.push({ + execution: staticExecution(), + to: hardcoded(namingContract), + selector: hardcoded(hash.getSelectorFromName("address_to_domain")), + calldata: [hardcoded(address)], + }); + + const calls = [ + { + execution: staticExecution(), + to: hardcoded(namingContract), + selector: hardcoded(hash.getSelectorFromName("domain_to_id")), + calldata: [arrayReference(0, 0)], + }, + { + execution: staticExecution(), + to: hardcoded(identityContract), + selector: hardcoded(hash.getSelectorFromName("get_verifier_data")), + calldata: [ + reference(1, 0), + hardcoded(shortString.encodeShortString("twitter")), + hardcoded(contracts["verifier"] as string), + hardcoded("0"), + ], + }, + { + execution: staticExecution(), + to: hardcoded(identityContract), + selector: hardcoded(hash.getSelectorFromName("get_verifier_data")), + calldata: [ + reference(1, 0), + hardcoded(shortString.encodeShortString("github")), + hardcoded(contracts["verifier"] as string), + hardcoded("0"), + ], + }, + { + execution: staticExecution(), + to: hardcoded(identityContract), + selector: hardcoded(hash.getSelectorFromName("get_verifier_data")), + calldata: [ + reference(1, 0), + hardcoded(shortString.encodeShortString("discord")), + hardcoded(contracts["verifier"] as string), + hardcoded("0"), + ], + }, + { + execution: staticExecution(), + to: hardcoded(identityContract), + selector: hardcoded(hash.getSelectorFromName("get_verifier_data")), + calldata: [ + reference(1, 0), + hardcoded(shortString.encodeShortString("proof_of_personhood")), + hardcoded(contracts["verifier_pop"] as string), + hardcoded("0"), + ], + }, + // PFP + { + execution: staticExecution(), + to: hardcoded(identityContract), + selector: hardcoded(hash.getSelectorFromName("get_verifier_data")), + calldata: [ + reference(1, 0), + hardcoded(shortString.encodeShortString("nft_pp_contract")), + hardcoded(contracts["verifier_pfp"] as string), + hardcoded("0"), + ], + }, + { + execution: staticExecution(), + to: hardcoded(identityContract), + selector: hardcoded( + hash.getSelectorFromName("get_extended_verifier_data") + ), + calldata: [ + reference(1, 0), + hardcoded(shortString.encodeShortString("nft_pp_id")), + hardcoded("2"), + hardcoded(contracts["verifier_pfp"] as string), + hardcoded("0"), + ], + }, + { + execution: notEqual(6, 0, 0), + to: reference(6, 0), + selector: hardcoded(hash.getSelectorFromName("tokenURI")), + calldata: [reference(7, 1), reference(7, 2)], + }, + ]; + initialCalldata.push(...calls); + fallbackCalldata.push(...calls); + + return { initialCalldata, fallbackCalldata }; +}; + const multicallABI = [ { type: "impl", From 89cbfe0c71f8d6fd6ce75090cddb173c1d7b9f27 Mon Sep 17 00:00:00 2001 From: Iris Date: Thu, 2 May 2024 10:52:29 +0200 Subject: [PATCH 2/2] dev: add changeset --- .changeset/hot-buses-bow.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/hot-buses-bow.md diff --git a/.changeset/hot-buses-bow.md b/.changeset/hot-buses-bow.md new file mode 100644 index 00000000..bcacdf94 --- /dev/null +++ b/.changeset/hot-buses-bow.md @@ -0,0 +1,5 @@ +--- +"@starknet-react/core": minor +--- + +Fix starknetID hooks to work with latest version of StarknetID contracts