From 894a2312c9c10cce5a7f780576900e67f3de6cf5 Mon Sep 17 00:00:00 2001 From: Iris Date: Mon, 17 Jun 2024 15:40:44 +0200 Subject: [PATCH] fix: check accounter interface implemented by account & add support for execute_outside_v2 tx --- app/components/recoverTokenModal.tsx | 9 ++--- app/components/tryAgainModal.tsx | 6 ++-- app/page.tsx | 25 ++++++++----- hooks/getTxVersion.tsx | 48 +++++++++++++++++++++++++ services/apiService.ts | 24 ++++++++++--- utils/callData/typedData.ts | 53 ++++++++++++++++++++++++++++ 6 files changed, 143 insertions(+), 22 deletions(-) create mode 100644 hooks/getTxVersion.tsx diff --git a/app/components/recoverTokenModal.tsx b/app/components/recoverTokenModal.tsx index 0822de7..ffe8872 100644 --- a/app/components/recoverTokenModal.tsx +++ b/app/components/recoverTokenModal.tsx @@ -47,10 +47,7 @@ const RecoverTokenModal: FunctionComponent = ({ you'll need to regenerate a signature with your ethereum wallet. ) : ( - <> - You can now install one of our partner wallets to get your additional - click! - + <>You can now install your Argent wallet to get your additional click! ); return ( @@ -97,13 +94,13 @@ const RecoverTokenModal: FunctionComponent = ({ > Argent - + */} )} diff --git a/app/components/tryAgainModal.tsx b/app/components/tryAgainModal.tsx index 0254c63..5140fe0 100644 --- a/app/components/tryAgainModal.tsx +++ b/app/components/tryAgainModal.tsx @@ -10,8 +10,6 @@ import WalletIcon from "./iconComponents/walletIcon"; import { getArgentIcon, getArgentWebsite, - getBraavosIcon, - getBraavosWebsite, } from "@/utils/starknetConnectorsWrapper"; type TryAgainModalProps = { @@ -76,13 +74,13 @@ const TryAgainModal: FunctionComponent = ({ > Argent - + */} ) : (
diff --git a/app/page.tsx b/app/page.tsx index a8974c0..bd0c608 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -26,7 +26,7 @@ import { } from "@/utils/dataService"; import WelcomeModal from "./components/welcomeModal"; import { getOutsideExecution } from "@/services/clickService"; -import { getTypedData } from "@/utils/callData/typedData"; +import { getTypedData, getTypedDataV2 } from "@/utils/callData/typedData"; import { altStarknetNewAccount, ethResetButton, @@ -36,7 +36,7 @@ import { starknetResetButtonFromEth, trackId, } from "@/services/apiService"; -import { Signature, TypedData } from "starknet"; +import { Signature, TypedData, WeierstrassSignatureType } from "starknet"; import { addEthToken, clearEthTokens, @@ -50,6 +50,7 @@ import Notification from "./components/notification"; import { hexToDecimal } from "@/utils/feltService"; import getPriceValue from "@/hooks/getEthQuote"; import RecoverTokenModal from "./components/recoverTokenModal"; +import getTxVersion from "@/hooks/getTxVersion"; export default function Home() { const [isLoaded, setIsLoaded] = useState(false); @@ -87,6 +88,8 @@ export default function Home() { const { hasEthTokens, ethTokens } = canPlayOnStarknet(network); const deploymentData = isStarknetDeployed(network, address); const isFinished = isOver5mn(countdownTimestamp); + const txVersion = getTxVersion(network, address); + // console.log("txVersion", txVersion); useEffect(() => { if (evmConnected) { @@ -238,14 +241,13 @@ export default function Home() { ) => { try { if (!starknetAccount) return; - console.log("starknetAccount", starknetAccount); const signature = await starknetAccount.signMessage(typedData); - console.log("signature", signature); const virtualTxId = await starknetResetButton( starknetAccount?.address as string, - signature as Signature, + signature, nonce, - executeBefore + executeBefore, + txVersion as number ); storeVirtualTxId(virtualTxId.virtual_tx_id); setTrackingList([...trackingList, virtualTxId.virtual_tx_id]); @@ -272,7 +274,8 @@ export default function Home() { signature as Signature, nonce, executeBefore, - availableDomain + availableDomain, + txVersion as number ); storeVirtualTxId(virtualTxId.virtual_tx_id); setTrackingList([...trackingList, virtualTxId.virtual_tx_id]); @@ -295,6 +298,7 @@ export default function Home() { ethTokens, nonce, executeBefore, + txVersion as number, deploymentData ); console.log("virtualTxId", virtualTxId); @@ -318,6 +322,7 @@ export default function Home() { ethSig.sig, nonce, executeBefore, + txVersion as number, deploymentData ); console.log("virtualTxId", virtualTxId); @@ -337,7 +342,11 @@ export default function Home() { const nonce = Math.floor(Math.random() * 1000000000000); const executeBefore = Math.floor(Date.now() / 1000) + 3600 * 48; // + 48h for testing const outsideExecution = getOutsideExecution(nonce, executeBefore); - const typedData = getTypedData(outsideExecution); + if (!txVersion) return; + const typedData = + txVersion === 1 + ? getTypedData(outsideExecution) + : getTypedDataV2(outsideExecution); if (remainingClicks.eligibilityAmt && remainingClicks.eligibilityAmt > 0) { await handleEligibleStarknetReset(typedData, nonce, executeBefore); diff --git a/hooks/getTxVersion.tsx b/hooks/getTxVersion.tsx new file mode 100644 index 0000000..01ee686 --- /dev/null +++ b/hooks/getTxVersion.tsx @@ -0,0 +1,48 @@ +import { NetworkType } from "@/constants/types"; +import { useAccount } from "@starknet-react/core"; +import { useEffect, useState } from "react"; + +export default function getTxVersion(network?: NetworkType, address?: string) { + const { account } = useAccount(); + const [txVersion, setVersion] = useState(); + + useEffect(() => { + if (!network || network === NetworkType.EVM || !account) { + console.log("1"); + setVersion(undefined); + return; + } + + const checkIsDeployed = async () => { + try { + const supports_v1 = await account.callContract({ + contractAddress: account.address, + entrypoint: "supports_interface", + calldata: [ + "0x68cfd18b92d1907b8ba3cc324900277f5a3622099431ea85dd8089255e4181", + ], + }); + + const supports_v2 = await account.callContract({ + contractAddress: account.address, + entrypoint: "supports_interface", + calldata: [ + "0x1d1144bb2138366ff28d8e9ab57456b1d332ac42196230c3a602003c89872", + ], + }); + + // @ts-ignore + if (Number(supports_v1.result[0])) setVersion(1); + // @ts-ignore + else if (Number(supports_v2.result[0])) setVersion(2); + else setVersion(undefined); + } catch (error) { + setVersion(undefined); + } + }; + + checkIsDeployed(); + }, [network, address]); + + return txVersion; +} diff --git a/services/apiService.ts b/services/apiService.ts index 003917c..f19497d 100644 --- a/services/apiService.ts +++ b/services/apiService.ts @@ -1,5 +1,11 @@ import { EthToken, GetDeploymentDataResult } from "@/constants/types"; -import { Signature, stark } from "starknet"; +import { + ArraySignatureType, + Signature, + WeierstrassSignatureType, + num, + stark, +} from "starknet"; const baseurl = process.env.NEXT_PUBLIC_ETH_BUTTON_API; const starknetIdBaseUrl = process.env.NEXT_PUBLIC_STARKNET_ID_API; @@ -67,8 +73,11 @@ export const starknetResetButton = async ( address: string, sig: Signature, nonce: number, - executeBefore: number + executeBefore: number, + version: number ) => { + const sigHex = stark.signatureToHexArray(sig); + console.log("sigHex reset button", sigHex); try { const response = await fetch(`${baseurl}/starknet_reset_button`, { method: "POST", @@ -77,9 +86,10 @@ export const starknetResetButton = async ( }, body: JSON.stringify({ addr: address, - sig: stark.signatureToHexArray(sig), + sig: [sigHex[1], sigHex[2]], nonce, execute_before: executeBefore, + version, }), }); return await response.json(); @@ -92,7 +102,8 @@ export const starknetDomainResetButton = async ( sig: Signature, nonce: number, executeBefore: number, - domain: string + domain: string, + version: number ) => { try { const response = await fetch(`${baseurl}/starknet_domain_reset_button`, { @@ -105,6 +116,7 @@ export const starknetDomainResetButton = async ( sig: stark.signatureToHexArray(sig), nonce, execute_before: executeBefore, + version, }), }); return await response.json(); @@ -119,6 +131,7 @@ export const starknetResetButtonFromEth = async ( tokens: EthToken[], nonce: number, executeBefore: number, + version: number, deploymentData?: GetDeploymentDataResult ) => { try { @@ -136,6 +149,7 @@ export const starknetResetButtonFromEth = async ( class_hash: deploymentData?.class_hash, salt: deploymentData?.salt, deployment_calldata: deploymentData?.calldata, + version, }), }); return await response.json(); @@ -151,6 +165,7 @@ export const altStarknetNewAccount = async ( eth_sig: String, nonce: number, executeBefore: number, + version: number, deploymentData?: GetDeploymentDataResult ) => { try { @@ -169,6 +184,7 @@ export const altStarknetNewAccount = async ( class_hash: deploymentData?.class_hash, salt: deploymentData?.salt, deployment_calldata: deploymentData?.calldata, + version, }), }); return await response.json(); diff --git a/utils/callData/typedData.ts b/utils/callData/typedData.ts index 594817d..19ef3f1 100644 --- a/utils/callData/typedData.ts +++ b/utils/callData/typedData.ts @@ -61,3 +61,56 @@ export function getTypedData(outsideExecution: OutsideExecution) { }, }; } + +const typesV2 = { + StarknetDomain: [ + { name: "name", type: "shortstring" }, + { name: "version", type: "shortstring" }, + { name: "chainId", type: "shortstring" }, + { name: "revision", type: "shortstring" }, + ], + OutsideExecution: [ + { name: "Caller", type: "ContractAddress" }, + { name: "Nonce", type: "felt" }, + { name: "Execute After", type: "u128" }, + { name: "Execute Before", type: "u128" }, + { name: "Calls", type: "Call*" }, + ], + Call: [ + { name: "To", type: "ContractAddress" }, + { name: "Selector", type: "selector" }, + { name: "Calldata", type: "felt*" }, + ], +}; + +export function getDomainV2(chainId: string) { + return { + name: "Account.execute_from_outside", + version: "2", + chainId: chainId, + revision: "1", + }; +} + +export function getTypedDataV2(outsideExecution: OutsideExecution) { + const chainId = + process.env.NEXT_PUBLIC_IS_TESTNET === "true" ? "SN_SEPOLIA" : "SN_MAIN"; + return { + types: typesV2, + primaryType: "OutsideExecution", + domain: getDomainV2(chainId), + message: { + Caller: outsideExecution.caller, + Nonce: outsideExecution.nonce, + "Execute After": outsideExecution.execute_after, + "Execute Before": outsideExecution.execute_before, + Calls: outsideExecution.calls.map((call) => { + return { + To: call.to, + Selector: call.selector, + Calldata: call.calldata, + }; + }), + }, + }; +}