From df864712f933306e3a60153795961cf6b0f8b4f4 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 25 Oct 2025 18:30:53 +0000 Subject: [PATCH 1/7] 0xsequence/master (#79) * Create fortify.yml * Update issue templates * Update CNAME * fix: upgrade @tanstack/react-query from 5.45.1 to 5.64.2 Snyk has created this PR to upgrade @tanstack/react-query from 5.45.1 to 5.64.2. See this package in npm: @tanstack/react-query See this project in Snyk: https://app.snyk.io/org/dargon789/project/bb845543-cbee-4e11-8cf9-8bfdf9205bf1?utm_source=github&utm_medium=referral&page=upgrade-pr * Create config.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update fortify.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/feature_request.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: snyk-bot Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .circleci/config.yml | 26 +++++++ .github/ISSUE_TEMPLATE/bug_report.md | 38 ++++++++++ .github/ISSUE_TEMPLATE/custom.md | 10 +++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++ .github/workflows/fortify.yml | 85 +++++++++++++++++++++++ CNAME | 2 +- wagmi-project/package.json | 2 +- 7 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 .circleci/config.yml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/custom.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/workflows/fortify.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000..709c9a7474 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/hardhat-project/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..81c7b58d88 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: 'bug' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000000..48d5f81fa4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..36014cde56 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: 'enhancement' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/fortify.yml b/.github/workflows/fortify.yml new file mode 100644 index 0000000000..ff1a25baf2 --- /dev/null +++ b/.github/workflows/fortify.yml @@ -0,0 +1,85 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +################################################################################################################################################ +# Fortify Application Security provides your team with solutions to empower DevSecOps practices, enable cloud transformation, and secure your # +# software supply chain. To learn more about Fortify, start a free trial or contact our sales team, visit fortify.com. # +# # +# Use this starter workflow as a basis for integrating Fortify Application Security Testing into your GitHub workflows. This template # +# demonstrates the steps to package the code+dependencies, initiate a scan, and optionally import SAST vulnerabilities into GitHub Security # +# Code Scanning Alerts. Additional information is available in the workflow comments and the Fortify AST Action / fcli / Fortify product # +# documentation. If you need additional assistance, please contact Fortify support. # +################################################################################################################################################ + +name: Fortify AST Scan + +# Customize trigger events based on your DevSecOps process and/or policy +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '31 12 * * 4' + workflow_dispatch: + +jobs: + Fortify-AST-Scan: + # Use the appropriate runner for building your source code. Ensure dev tools required to build your code are present and configured appropriately (MSBuild, Python, etc). + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + # Check out source code + - name: Check Out Source Code + uses: actions/checkout@v4 + + # Java is required to run the various Fortify utilities. Ensuring proper version is installed on the runner. + - name: Setup Java + uses: actions/setup-java@v4 + with: + java-version: 17 + distribution: 'temurin' + + # Perform SAST and optionally SCA scan via Fortify on Demand/Fortify Hosted/Software Security Center, then + # optionally export SAST results to the GitHub code scanning dashboard. In case further customization is + # required, you can use sub-actions like fortify/github-action/setup@v1 to set up the various Fortify tools + # and run them directly from within your pipeline; see https://github.com/fortify/github-action#readme for + # details. + + - name: Run FoD SAST Scan + uses: fortify/github-action@a92347297e02391b857e7015792cd1926a4cd418 + with: + sast-scan: true + env: + ### Required configuration when integrating with Fortify on Demand + FOD_URL: https://ams.fortify.com + FOD_TENANT: ${{secrets.FOD_TENANT}} + FOD_USER: ${{secrets.FOD_USER}} + FOD_PASSWORD: ${{secrets.FOD_PAT}} + ### Optional configuration when integrating with Fortify on Demand + # EXTRA_PACKAGE_OPTS: -oss # Extra 'scancentral package' options, like '-oss'' if + # Debricked SCA scan is enabled on Fortify on Demand + # EXTRA_FOD_LOGIN_OPTS: --socket-timeout=60s # Extra 'fcli fod session login' options + # FOD_RELEASE: MyApp:MyRelease # FoD release name, default: /:; may + # replace app+release name with numeric release ID + # DO_WAIT: true # Wait for scan completion, implied if 'DO_EXPORT: true' + # DO_EXPORT: true # Export SAST results to GitHub code scanning dashboard + ### Required configuration when integrating with Fortify Hosted / Software Security Center & ScanCentral + # SSC_URL: ${{secrets.SSC_URL}} # SSC URL + # SSC_TOKEN: ${{secrets.SSC_TOKEN}} # SSC CIToken or AutomationToken + # SC_SAST_TOKEN: ${{secrets.SC_SAST_TOKEN}} # ScanCentral SAST client auth token + # SC_SAST_SENSOR_VERSION: ${{vars.SC_SAST_SENSOR_VERSION}} # Sensor version on which to run the scan; + # usually defined as organization or repo variable + ### Optional configuration when integrating with Fortify Hosted / Software Security Center & ScanCentral + # EXTRA_SC_SAST_LOGIN_OPTS: --socket-timeout=60s # Extra 'fcli sc-sast session login' options + # SSC_APPVERSION: MyApp:MyVersion # SSC application version, default: /: + # EXTRA_PACKAGE_OPTS: -bv myCustomPom.xml # Extra 'scancentral package' options + # DO_WAIT: true # Wait for scan completion, implied if 'DO_EXPORT: true' + # DO_EXPORT: true # Export SAST results to GitHub code scanning dashboard diff --git a/CNAME b/CNAME index c9903e895b..aa0085b19f 100644 --- a/CNAME +++ b/CNAME @@ -1 +1 @@ -wagmi-project-two.vercel.app \ No newline at end of file +sequence.app \ No newline at end of file diff --git a/wagmi-project/package.json b/wagmi-project/package.json index 298a76bbc6..206445bb31 100644 --- a/wagmi-project/package.json +++ b/wagmi-project/package.json @@ -10,7 +10,7 @@ "preview": "vite preview" }, "dependencies": { - "@tanstack/react-query": "5.45.1", + "@tanstack/react-query": "5.64.2", "react": "^18.3.1", "react-dom": "^18.3.1", "viem": "latest", From ffd17e627c539e2ddc61a77c5cb5350bb88b05b1 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 14 Nov 2025 14:18:21 +0700 Subject: [PATCH 2/7] Update tests.yml (#82) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d49eea9495..7d5edbd5f8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,6 +9,7 @@ jobs: steps: - uses: actions/checkout@v3 - uses: ./.github/actions/install-dependencies + - run: pnpm install --no-frozen-lockfile build: name: Run build From f3427844b10ece0c3a997657ffb6b49c8f14a77c Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 17 Nov 2025 19:47:56 +0700 Subject: [PATCH 3/7] Update App.tsx (#92) Summary by Sourcery Overhaul App.tsx to build a comprehensive Sequence Wallet demo application, replacing wagmi with Sequence SDK integration, adding environment/configuration management, rich wallet operations, and a structured UI with console output for interactive testing New Features: Replace wagmi-based hooks with @0xsequence wallet initialization and integration Add environment selection and dynamic wallet URLs via query parameters Implement connect, disconnect, open/close wallet and customizable connection settings Provide extensive demo actions including chain/network switching, account/balance queries, message signing, typed data signing, and transaction sending Introduce a console component and logging for viewing function outputs Add email-based auto-login via modal with validation Enhancements: Refactor UI to use design-system components and group actions thematically Initialize logger and configure default chain/network Memoize and listen to wallet events such as chain changes Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- wagmi-project/src/App.tsx | 1380 +++++++++++++++++++++++++++++++++++-- 1 file changed, 1341 insertions(+), 39 deletions(-) diff --git a/wagmi-project/src/App.tsx b/wagmi-project/src/App.tsx index faa8ce1c79..05da5e5b34 100644 --- a/wagmi-project/src/App.tsx +++ b/wagmi-project/src/App.tsx @@ -1,46 +1,1348 @@ -import { useAccount, useConnect, useDisconnect } from 'wagmi' +import { AnimatePresence } from 'framer-motion' +import React, { useState, useEffect, useMemo, SetStateAction } from 'react' +import { ethers } from 'ethers' +import { sequence } from '0xsequence' +import { walletContracts } from '@0xsequence/abi' +import { + Box, + Image, + Text, + Button, + ExternalLinkIcon, + Divider, + Card, + TransactionIcon, + Select, + TokenImage, + TextInput, + Modal +} from '@0xsequence/design-system' +import { ETHAuth } from '@0xsequence/ethauth' +import { configureLogger } from '@0xsequence/utils' +import { ConnectOptions, OpenWalletIntent, Settings } from '@0xsequence/provider' +import { ChainId, NetworkType } from '@0xsequence/network' -function App() { - const account = useAccount() - const { connectors, connect, status, error } = useConnect() - const { disconnect } = useDisconnect() +import { ERC_20_ABI } from './constants/abi' +import { Console } from './components/Console' +import { Group } from './components/Group' +import { getDefaultChainId, toHexString } from './helpers' +import logoUrl from './images/logo.svg' +import skyweaverBannerUrl from './images/skyweaver-banner.png' +import skyweaverBannerLargeUrl from './images/skyweaver-banner-large.png' + +configureLogger({ logLevel: 'DEBUG' }) + +interface Environment { + name: string + walletUrl: string + projectAccessKey: string +} + +const environments: Environment[] = [ + { + name: 'production', + walletUrl: 'https://sequence.app', + projectAccessKey: 'AQAAAAAAAAbvrgpWEC2Aefg5qYStQmwjBpA' + }, + { + name: 'development', + walletUrl: 'https://dev.sequence.app', + //projectAccessKey: 'AQAAAAAAAAVBNfoB30kz7Ph4I_Qs5mkYuDc', + projectAccessKey: 'AQAAAAAAAAVCXiQ9f_57R44MjorZ4SmGdhA' + }, + { + name: 'local', + walletUrl: 'http://localhost:3333', + projectAccessKey: 'AQAAAAAAAAVCXiQ9f_57R44MjorZ4SmGdhA' + }, + { + name: 'custom', + walletUrl: '', + projectAccessKey: '' + } +] + +const DEFAULT_API_URL = 'https://api.sequence.app' + +// Specify your desired default chain id. NOTE: you can communicate to multiple +// chains at the same time without even having to switch the network, but a default +// chain is required. +const defaultChainId = getDefaultChainId() || ChainId.MAINNET +// const defaultChainId = ChainId.POLYGON +// const defaultChainId = ChainId.GOERLI +// const defaultChainId = ChainId.ARBITRUM +// const defaultChainId = ChainId.AVALANCHE +// etc.. see the full list here: https://docs.sequence.xyz/multi-chain-support + +// For Sequence core dev team -- app developers can ignore +// a custom wallet app url can specified in the query string +const urlParams = new URLSearchParams(window.location.search) + +const env = urlParams.get('env') ?? 'production' +const envConfig = environments.find(x => x.name === env) +const walletAppURL = urlParams.get('walletAppURL') ?? envConfig.walletUrl +const projectAccessKey = urlParams.get('projectAccessKey') ?? envConfig.projectAccessKey +const showProhibitedActions = urlParams.has('showProhibitedActions') + +const isCustom = walletAppURL !== envConfig.walletUrl || projectAccessKey !== envConfig.projectAccessKey + +if (walletAppURL && walletAppURL.length > 0) { + // Wallet can point to a custom wallet app url + // NOTICE: this is not needed, unless testing an alpha version of the wallet + sequence.initWallet(projectAccessKey, { defaultNetwork: defaultChainId, transports: { walletAppURL } }) +} else { + // Init the sequence wallet library at the top-level of your project with + // your designed default chain id + sequence.initWallet(projectAccessKey, { defaultNetwork: defaultChainId, transports: { walletAppURL } }) +} + +// App component +const App = () => { + const [consoleMsg, setConsoleMsg] = useState(null) + const [email, setEmail] = useState(null) + const [consoleLoading, setConsoleLoading] = useState(false) + const [isWalletConnected, setIsWalletConnected] = useState(false) + + const wallet = sequence.getWallet().getProvider() + + const [showChainId, setShowChainId] = useState(wallet.getChainId()) + const [isOpen, toggleModal] = useState(false) + const [warning, setWarning] = useState(false) + + useMemo(() => { + wallet.on('chainChanged', (chainId: string) => { + setShowChainId(Number(BigInt(chainId))) + }) + }, []) + + useEffect(() => { + setIsWalletConnected(wallet.isConnected()) + }, [wallet]) + + useEffect(() => { + consoleWelcomeMessage() + // eslint-disable-next-line + }, [isWalletConnected]) + + useEffect(() => { + // Wallet events + wallet.client.onOpen(() => { + console.log('wallet window opened') + }) + + wallet.client.onClose(() => { + console.log('wallet window closed') + }) + }, [wallet]) + + const defaultConnectOptions: ConnectOptions = { + app: 'Demo Dapp', + askForEmail: true + // keepWalletOpened: true, + } + + // Methods + const connect = async (connectOptions: ConnectOptions = { app: 'Demo dapp' }) => { + if (isWalletConnected) { + resetConsole() + appendConsoleLine('Wallet already connected!') + setConsoleLoading(false) + return + } + + connectOptions = { + ...defaultConnectOptions, + ...connectOptions, + settings: { + ...defaultConnectOptions.settings, + ...connectOptions.settings + } + } + + try { + resetConsole() + appendConsoleLine('Connecting') + const wallet = sequence.getWallet() + + const connectDetails = await wallet.connect(connectOptions) + + // Example of how to verify using ETHAuth via Sequence API + if (connectOptions.authorize && connectDetails.connected) { + let apiUrl = urlParams.get('apiUrl') + + if (!apiUrl || apiUrl.length === 0) { + apiUrl = DEFAULT_API_URL + } + + const api = new sequence.api.SequenceAPIClient(apiUrl) + // or just + // const api = new sequence.api.SequenceAPIClient('https://api.sequence.app') + + const { isValid } = await api.isValidETHAuthProof({ + chainId: connectDetails.chainId, + walletAddress: connectDetails.session.accountAddress, + ethAuthProofString: connectDetails.proof!.proofString + }) + + appendConsoleLine(`isValid (API)?: ${isValid}`) + } + + // Example of how to verify using ETHAuth directl on the client + if (connectOptions.authorize) { + const ethAuth = new ETHAuth() + + if (connectDetails.proof) { + const decodedProof = await ethAuth.decodeProof(connectDetails.proof.proofString, true) + + const isValid = await wallet.utils.isValidTypedDataSignature( + wallet.getAddress(), + connectDetails.proof.typedData, + decodedProof.signature, + Number(BigInt(connectDetails.chainId)) + ) + + appendConsoleLine(`connected using chainId: ${BigInt(connectDetails.chainId).toString()}`) + appendConsoleLine(`isValid (client)?: ${isValid}`) + } + } + + setConsoleLoading(false) + if (connectDetails.connected) { + appendConsoleLine('Wallet connected!') + appendConsoleLine(`shared email: ${connectDetails.email}`) + setIsWalletConnected(true) + } else { + appendConsoleLine('Failed to connect wallet - ' + connectDetails.error) + } + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const disconnect = () => { + const wallet = sequence.getWallet() + wallet.disconnect() + consoleWelcomeMessage() + setIsWalletConnected(false) + } + + const openWallet = () => { + const wallet = sequence.getWallet() + wallet.openWallet() + } + + const openWalletWithSettings = () => { + const wallet = sequence.getWallet() + + const settings: Settings = { + theme: 'light', + includedPaymentProviders: ['moonpay', 'ramp'], + defaultFundingCurrency: 'eth', + defaultPurchaseAmount: 400, + lockFundingCurrencyToDefault: false + } + + const intent: OpenWalletIntent = { + type: 'openWithOptions', + options: { + app: 'Demo Dapp', + settings + } + } + + const path = 'wallet/add-funds' + wallet.openWallet(path, intent) + } + + const closeWallet = () => { + const wallet = sequence.getWallet() + wallet.closeWallet() + } + + const isConnected = async () => { + resetConsole() + const wallet = sequence.getWallet() + appendConsoleLine(`isConnected?: ${wallet.isConnected()}`) + setConsoleLoading(false) + } + + const isOpened = async () => { + resetConsole() + const wallet = sequence.getWallet() + appendConsoleLine(`isOpened?: ${wallet.isOpened()}`) + setConsoleLoading(false) + } + + const getChainID = async () => { + try { + resetConsole() + + const topChainId = wallet.getChainId() + appendConsoleLine(`top chainId: ${topChainId}`) + + const provider = wallet.getProvider() + const providerChainId = provider!.getChainId() + appendConsoleLine(`provider.getChainId(): ${providerChainId}`) + + const signer = wallet.getSigner() + const signerChainId = await signer.getChainId() + appendConsoleLine(`signer.getChainId(): ${signerChainId}`) + + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const getAccounts = async () => { + try { + resetConsole() + + const wallet = sequence.getWallet() + const address = wallet.getAddress() + appendConsoleLine(`getAddress(): ${address}`) + + const provider = wallet.getProvider() + const accountList = provider.listAccounts() + appendConsoleLine(`accounts: ${JSON.stringify(accountList)}`) + + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const getBalance = async () => { + try { + resetConsole() + + const wallet = sequence.getWallet() + + const provider = wallet.getProvider() + const account = wallet.getAddress() + const balanceChk1 = await provider!.getBalance(account) + appendConsoleLine(`balance check 1: ${balanceChk1.toString()}`) + + const signer = wallet.getSigner() + const balanceChk2 = await signer.getBalance() + appendConsoleLine(`balance check 2: ${balanceChk2.toString()}`) + + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const getNetworks = async () => { + try { + resetConsole() + + const wallet = sequence.getWallet() + const networks = await wallet.getNetworks() + + appendConsoleLine(`networks: ${JSON.stringify(networks, null, 2)}`) + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const signMessageString = async () => { + try { + resetConsole() + + const wallet = sequence.getWallet() + + appendConsoleLine('signing message...') + const signer = wallet.getSigner() + + const message = `1915 Robert Frost +The Road Not Taken + +Two roads diverged in a yellow wood, +And sorry I could not travel both +And be one traveler, long I stood +And looked down one as far as I could +To where it bent in the undergrowth + +Then took the other, as just as fair, +And having perhaps the better claim, +Because it was grassy and wanted wear +Though as for that the passing there +Had worn them really about the same, + +And both that morning equally lay +In leaves no step had trodden black. +Oh, I kept the first for another day! +Yet knowing how way leads on to way, +I doubted if I should ever come back. + +I shall be telling this with a sigh +Somewhere ages and ages hence: +Two roads diverged in a wood, and I— +I took the one less traveled by, +And that has made all the difference. + +\u2601 \u2600 \u2602` + + // sign + const sig = await signer.signMessage(message) + appendConsoleLine(`signature: ${sig}`) + + const isValid = await wallet.utils.isValidMessageSignature(wallet.getAddress(), message, sig, await signer.getChainId()) + appendConsoleLine(`isValid?: ${isValid}`) + if (!isValid) throw new Error('sig invalid') + + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const signMessageHex = async () => { + try { + resetConsole() + + const wallet = sequence.getWallet() + + appendConsoleLine('signing message...') + const signer = wallet.getSigner() + + // Message in hex + const message = ethers.hexlify(ethers.toUtf8Bytes('Hello, world!')) + + // sign + const sig = await signer.signMessage(message) + appendConsoleLine(`signature: ${sig}`) + + const isValid = await wallet.utils.isValidMessageSignature(wallet.getAddress(), message, sig, await signer.getChainId()) + appendConsoleLine(`isValid?: ${isValid}`) + if (!isValid) throw new Error('sig invalid') + + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const signMessageBytes = async () => { + try { + resetConsole() + + const wallet = sequence.getWallet() + + appendConsoleLine('signing message...') + const signer = wallet.getSigner() + + // Message in hex + const message = ethers.toUtf8Bytes('Hello, world!') + + // sign + const sig = await signer.signMessage(message) + appendConsoleLine(`signature: ${sig}`) + + const isValid = await wallet.utils.isValidMessageSignature(wallet.getAddress(), message, sig, await signer.getChainId()) + appendConsoleLine(`isValid?: ${isValid}`) + if (!isValid) throw new Error('sig invalid') + + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const signTypedData = async () => { + try { + resetConsole() + const wallet = sequence.getWallet() + + appendConsoleLine('signing typedData...') + + const typedData: sequence.utils.TypedData = { + types: { + Person: [ + { name: 'name', type: 'string' }, + { name: 'wallet', type: 'address' } + ], + Mail: [ + { name: 'from', type: 'Person' }, + { name: 'to', type: 'Person' }, + { name: 'cc', type: 'Person[]' }, + { name: 'contents', type: 'string' }, + { name: 'attachements', type: 'string[]' } + ] + }, + primaryType: 'Mail', + domain: { + name: 'Ether Mail', + version: '1', + chainId: 1, + verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC' + }, + message: { + from: { + name: 'Cow', + wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826' + }, + to: { + name: 'Bob', + wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB' + }, + cc: [ + { name: 'Dev Team', wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB' }, + { name: 'Accounting', wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB' } + ], + contents: 'Hello, Bob!', + attachements: ['cat.png', 'dog.png'] + } + } + + const signer = wallet.getSigner() + + const sig = await signer.signTypedData(typedData.domain, typedData.types, typedData.message) + appendConsoleLine(`signature: ${sig}`) + + // validate + const isValid = await wallet.utils.isValidTypedDataSignature(wallet.getAddress(), typedData, sig, await signer.getChainId()) + appendConsoleLine(`isValid?: ${isValid}`) + + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const estimateUnwrapGas = async () => { + try { + resetConsole() + + const wallet = sequence.getWallet() + + const wmaticContractAddress = '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270' + const wmaticInterface = new ethers.Interface(['function withdraw(uint256 amount)']) + + const tx: sequence.transactions.Transaction = { + to: wmaticContractAddress, + data: wmaticInterface.encodeFunctionData('withdraw', ['1000000000000000000']) + } + + const provider = wallet.getProvider() + const estimate = await provider.estimateGas(tx) + + appendConsoleLine(`estimated gas needed for wmatic withdrawal : ${estimate.toString()}`) + + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const sendETH = async (signer?: sequence.provider.SequenceSigner) => { + try { + resetConsole() + const wallet = sequence.getWallet() + + signer = signer || wallet.getSigner() + + appendConsoleLine(`Transfer txn on ${signer.getChainId()} chainId`) + + // NOTE: on mainnet, the balance will be of ETH value + // and on matic, the balance will be of MATIC value + + // Sending the funds to the wallet itself + // so we don't lose any funds ;-) + // (of course, you can send anywhere) + const toAddress = await signer.getAddress() + + const tx1: sequence.transactions.Transaction = { + delegateCall: false, + revertOnError: false, + gasLimit: '0x55555', + to: toAddress, + value: ethers.parseEther('1.234'), + data: '0x' + } + + const tx2: sequence.transactions.Transaction = { + delegateCall: false, + revertOnError: false, + gasLimit: '0x55555', + to: toAddress, + value: ethers.parseEther('0.4242'), + data: '0x' + } + + const provider = signer.provider + + const balance1 = await provider.getBalance(toAddress) + appendConsoleLine(`balance of ${toAddress}, before: ${balance1}`) + + const txnResp = await signer.sendTransaction([tx1, tx2]) + appendConsoleLine(`txnResponse: ${JSON.stringify(txnResp)}`) + + const balance2 = await provider.getBalance(toAddress) + appendConsoleLine(`balance of ${toAddress}, after: ${balance2}`) + + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const sendSepoliaUSDC = async (signer?: sequence.provider.SequenceSigner) => { + try { + resetConsole() + + const wallet = sequence.getWallet() + + signer = signer || wallet.getSigner() // select DefaultChain signer by default + + // Sending the funds to the wallet itself + // so we don't lose any funds ;-) + // (of course, you can send anywhere) + const toAddress = await signer.getAddress() + + const amount = ethers.parseUnits('1', 1) + + // (USDC address on Sepolia) + const usdcAddress = '0x07865c6e87b9f70255377e024ace6630c1eaa37f' + + const tx: sequence.transactions.Transaction = { + delegateCall: false, + revertOnError: false, + gasLimit: '0x55555', + to: usdcAddress, + value: 0, + data: new ethers.Interface(ERC_20_ABI).encodeFunctionData('transfer', [toAddress, toHexString(amount)]) + } + + const txnResp = await signer.sendTransaction([tx], { chainId: ChainId.SEPOLIA }) + appendConsoleLine(`txnResponse: ${JSON.stringify(txnResp)}`) + + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const sendDAI = async (signer?: sequence.provider.SequenceSigner) => { + try { + resetConsole() + + const wallet = sequence.getWallet() + + signer = signer || wallet.getSigner() // select DefaultChain signer by default + + // Sending the funds to the wallet itself + // so we don't lose any funds ;-) + // (of course, you can send anywhere) + const toAddress = await signer.getAddress() + + const amount = ethers.parseUnits('0.05', 18) + const daiContractAddress = '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063' // (DAI address on Polygon) + + const tx: sequence.transactions.Transaction = { + delegateCall: false, + revertOnError: false, + gasLimit: '0x55555', + to: daiContractAddress, + value: 0, + data: new ethers.Interface(ERC_20_ABI).encodeFunctionData('transfer', [toAddress, toHexString(amount)]) + } + + const txnResp = await signer.sendTransaction([tx]) + appendConsoleLine(`txnResponse: ${JSON.stringify(txnResp)}`) + + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const sendETHSidechain = async () => { + try { + const wallet = sequence.getWallet() + + // Send either to Arbitrum or Optimism + // just pick one that is not the current chainId + const pick = wallet.getChainId() === ChainId.ARBITRUM ? ChainId.OPTIMISM : ChainId.ARBITRUM + sendETH(wallet.getSigner(pick)) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const send1155Tokens = async () => { + try { + resetConsole() + appendConsoleLine('TODO') + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const contractExample = async (signer?: sequence.provider.SequenceSigner) => { + try { + resetConsole() + + const wallet = sequence.getWallet() + + signer = signer || wallet.getSigner() + + const abi = [ + 'function balanceOf(address owner) view returns (uint256)', + 'function decimals() view returns (uint8)', + 'function symbol() view returns (string)', + 'function transfer(address to, uint amount) returns (bool)', + 'event Transfer(address indexed from, address indexed to, uint amount)' + ] + + // USD Coin (PoS) on Polygon + const address = '0x2791bca1f2de4661ed88a30c99a7a9449aa84174' + + const usdc = new ethers.Contract(address, abi) + + const usdSymbol = await usdc.symbol() + appendConsoleLine(`Token symbol: ${usdSymbol}`) + + const balance = await usdc.balanceOf(await signer.getAddress()) + appendConsoleLine(`Token Balance: ${balance.toString()}`) + + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const fetchTokenBalances = async () => { + try { + resetConsole() + + const wallet = sequence.getWallet() + + const signer = wallet.getSigner() + const accountAddress = await signer.getAddress() + const networks = await wallet.getNetworks() + const network = networks.find(network => network.chainId === ChainId.POLYGON) + + if (!network) { + throw new Error(`Could not find Polygon network in networks list`) + } + + const indexer = new sequence.indexer.SequenceIndexer(network.indexerUrl) + + const tokenBalances = await indexer.getTokenBalances({ + accountAddress: accountAddress, + includeMetadata: true + }) + + appendConsoleLine(`tokens in your account: ${JSON.stringify(tokenBalances)}`) + + // NOTE: you can put any NFT/collectible address in the `contractAddress` field and it will return all of the balances + metadata. + // We use the Skyweaver production contract address here for demo purposes, but try another one :) + const skyweaverCollectibles = await indexer.getTokenBalances({ + accountAddress: accountAddress, + includeMetadata: true, + contractAddress: '0x631998e91476DA5B870D741192fc5Cbc55F5a52E' + }) + appendConsoleLine(`skyweaver collectibles in your account: ${JSON.stringify(skyweaverCollectibles)}`) + + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const updateImplementation = async (signer?: sequence.provider.SequenceSigner) => { + try { + resetConsole() + + const wallet = sequence.getWallet() + + signer = signer || wallet.getSigner() // select DefaultChain signer by default + + const transaction: sequence.transactions.Transaction = { + to: wallet.getAddress(), + data: new ethers.Interface(walletContracts.mainModule.abi).encodeFunctionData('updateImplementation', [ + ethers.ZeroAddress + ]) + } + + const response = await signer.sendTransaction(transaction) + appendConsoleLine(`response: ${JSON.stringify(response)}`) + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const updateImageHash = async (signer?: sequence.provider.SequenceSigner) => { + try { + resetConsole() + + const wallet = sequence.getWallet() + + signer = signer || wallet.getSigner() // select DefaultChain signer by default + + const transaction: sequence.transactions.Transaction = { + to: wallet.getAddress(), + data: new ethers.Interface(walletContracts.mainModuleUpgradable.abi).encodeFunctionData('updateImageHash', [ + ethers.ZeroHash + ]) + } + + const response = await signer.sendTransaction(transaction) + appendConsoleLine(`response: ${JSON.stringify(response)}`) + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const delegateCall = async (signer?: sequence.provider.SequenceSigner) => { + try { + resetConsole() + + const wallet = sequence.getWallet() + + signer = signer || wallet.getSigner() // select DefaultChain signer by default + + const transaction: sequence.transactions.Transaction = { + to: wallet.getAddress(), + delegateCall: true + } + + const response = await signer.sendTransaction(transaction) + appendConsoleLine(`response: ${JSON.stringify(response)}`) + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const addHook = async (signer?: sequence.provider.SequenceSigner) => { + try { + resetConsole() + + const wallet = sequence.getWallet() + + signer = signer || wallet.getSigner() // select DefaultChain signer by default + + const transaction: sequence.transactions.Transaction = { + to: wallet.getAddress(), + data: new ethers.Interface(['function addHook(bytes4 _signature, address _implementation)']).encodeFunctionData( + 'addHook', + ['0x01234567', ethers.ZeroAddress] + ) + } + + const response = await signer.sendTransaction(transaction) + appendConsoleLine(`response: ${JSON.stringify(response)}`) + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const setExtraImageHash = async (signer?: sequence.provider.SequenceSigner) => { + try { + resetConsole() + + const wallet = sequence.getWallet() + + signer = signer || wallet.getSigner() // select DefaultChain signer by default + + const transaction: sequence.transactions.Transaction = { + to: wallet.getAddress(), + data: new ethers.Interface(['function setExtraImageHash(bytes32 _imageHash, uint256 _expiration)']).encodeFunctionData( + 'setExtraImageHash', + [ethers.ZeroHash, ethers.MaxUint256] + ) + } + + const response = await signer.sendTransaction(transaction) + appendConsoleLine(`response: ${JSON.stringify(response)}`) + setConsoleLoading(false) + } catch (e) { + console.error(e) + consoleErrorMessage() + } + } + + const appendConsoleLine = (message: string, clear = false) => { + console.log(message) + + if (clear) { + return setConsoleMsg(message) + } + + return setConsoleMsg(prevState => { + return `${prevState}\n\n${message}` + }) + } + + const resetConsole = () => { + setConsoleLoading(true) + } + + const consoleWelcomeMessage = () => { + setConsoleLoading(false) + + if (isWalletConnected) { + setConsoleMsg('Status: Wallet is connected :)') + } else { + setConsoleMsg('Status: Wallet not connected. Please connect wallet first.') + } + } + + const consoleErrorMessage = () => { + setConsoleLoading(false) + setConsoleMsg('An error occurred') + } + + // networks list, filtered and sorted + const omitNetworks = [ + ChainId.RINKEBY, + ChainId.HARDHAT, + ChainId.HARDHAT_2, + ChainId.KOVAN, + ChainId.ROPSTEN, + ChainId.HOMEVERSE_TESTNET, + ChainId.BASE_GOERLI + ] + + const mainnets = Object.values(sequence.network.networks) + .filter(network => network.type === NetworkType.MAINNET) + .sort((a, b) => a.chainId - b.chainId) + const testnets = Object.values(sequence.network.networks) + .filter(network => network.type === NetworkType.TESTNET) + .sort((a, b) => a.chainId - b.chainId) + const networks = [...mainnets, ...testnets].filter(network => !network.deprecated && !omitNetworks.includes(network.chainId)) + + useEffect(() => { + if (email && !isOpen) { + console.log(email) + connect({ + app: 'Demo Dapp', + authorize: true, + settings: { + // Specify signInWithEmail with an email address to allow user automatically sign in with the email option. + signInWithEmail: email, + theme: 'dark', + bannerUrl: `${window.location.origin}${skyweaverBannerUrl}` + } + }) + setEmail(null) + } + }, [email, isOpen]) + + const sanitizeEmail = (email: string) => { + // Trim unnecessary spaces + email = email.trim() + + // Check if the email matches the pattern of a typical email + const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/ + if (emailRegex.test(email)) { + return true + } + + return false + } return ( - <> -
-

Account

- -
- status: {account.status} -
- addresses: {JSON.stringify(account.addresses)} -
- chainId: {account.chainId} -
- - {account.status === 'connected' && ( - + + + + logo + + + + + + Demo Dapp + + + + + + A dapp example on how to use the Sequence Wallet. This covers how to connect, sign messages and send transctions. + + + + + + + Please open your browser dev inspector to view output of functions below. + + + + + + {!isCustom && ( + + wallet.setDefaultChainId(Number(value))} + value={String(showChainId)} + options={[ + ...Object.values(networks).map(network => ({ + label: ( + + + {network.title!} + + ), + value: String(network.chainId) + })) + ]} + /> + + + +
- -
-

Connect

- {connectors.map((connector) => ( - - ))} -
{status}
-
{error?.message}
-
- + + + ) } -export default App +export default React.memo(App) From 28a268eef9f36a4bcf257d175c49c31f55f8008b Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 23 Dec 2025 18:14:59 +0700 Subject: [PATCH 4/7] Update wagmi-project/src/App.tsx Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- wagmi-project/src/App.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/wagmi-project/src/App.tsx b/wagmi-project/src/App.tsx index 05da5e5b34..236da51677 100644 --- a/wagmi-project/src/App.tsx +++ b/wagmi-project/src/App.tsx @@ -109,11 +109,15 @@ const App = () => { const [isOpen, toggleModal] = useState(false) const [warning, setWarning] = useState(false) - useMemo(() => { - wallet.on('chainChanged', (chainId: string) => { + useEffect(() => { + const handleChainChanged = (chainId: string) => { setShowChainId(Number(BigInt(chainId))) - }) - }, []) + } + wallet.on('chainChanged', handleChainChanged) + return () => { + wallet.off('chainChanged', handleChainChanged) + } + }, [wallet]) useEffect(() => { setIsWalletConnected(wallet.isConnected()) From 51a64321d3c0b91c9bd43c1e7e693f974c045dbb Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 23 Dec 2025 18:15:24 +0700 Subject: [PATCH 5/7] Update wagmi-project/src/App.tsx Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- wagmi-project/src/App.tsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/wagmi-project/src/App.tsx b/wagmi-project/src/App.tsx index 236da51677..5262628765 100644 --- a/wagmi-project/src/App.tsx +++ b/wagmi-project/src/App.tsx @@ -130,13 +130,20 @@ const App = () => { useEffect(() => { // Wallet events - wallet.client.onOpen(() => { + const onOpen = () => { console.log('wallet window opened') - }) + } + wallet.client.on('open', onOpen) - wallet.client.onClose(() => { + const onClose = () => { console.log('wallet window closed') - }) + } + wallet.client.on('close', onClose) + + return () => { + wallet.client.off('open', onOpen) + wallet.client.off('close', onClose) + } }, [wallet]) const defaultConnectOptions: ConnectOptions = { From 640be8ffe175c762828aded47cc76e8d2c404b56 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 23 Dec 2025 18:15:48 +0700 Subject: [PATCH 6/7] Update wagmi-project/src/App.tsx Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- wagmi-project/src/App.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/wagmi-project/src/App.tsx b/wagmi-project/src/App.tsx index 5262628765..2b4431b5e9 100644 --- a/wagmi-project/src/App.tsx +++ b/wagmi-project/src/App.tsx @@ -79,7 +79,10 @@ const defaultChainId = getDefaultChainId() || ChainId.MAINNET const urlParams = new URLSearchParams(window.location.search) const env = urlParams.get('env') ?? 'production' -const envConfig = environments.find(x => x.name === env) +const envConfig = environments.find(x => x.name === env) ?? environments.find(x => x.name === 'production') +if (!envConfig) { + throw new Error('Invalid environment configuration.') +} const walletAppURL = urlParams.get('walletAppURL') ?? envConfig.walletUrl const projectAccessKey = urlParams.get('projectAccessKey') ?? envConfig.projectAccessKey const showProhibitedActions = urlParams.has('showProhibitedActions') From 7fe4a922dfbd817fa76b8e30e9be184e6f38ddb1 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 23 Dec 2025 18:16:10 +0700 Subject: [PATCH 7/7] Update wagmi-project/src/App.tsx Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- wagmi-project/src/App.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wagmi-project/src/App.tsx b/wagmi-project/src/App.tsx index 2b4431b5e9..9f245fc45f 100644 --- a/wagmi-project/src/App.tsx +++ b/wagmi-project/src/App.tsx @@ -181,7 +181,7 @@ const App = () => { const connectDetails = await wallet.connect(connectOptions) // Example of how to verify using ETHAuth via Sequence API - if (connectOptions.authorize && connectDetails.connected) { + if (connectOptions.authorize && connectDetails.connected && connectDetails.proof) { let apiUrl = urlParams.get('apiUrl') if (!apiUrl || apiUrl.length === 0) { @@ -195,7 +195,7 @@ const App = () => { const { isValid } = await api.isValidETHAuthProof({ chainId: connectDetails.chainId, walletAddress: connectDetails.session.accountAddress, - ethAuthProofString: connectDetails.proof!.proofString + ethAuthProofString: connectDetails.proof.proofString }) appendConsoleLine(`isValid (API)?: ${isValid}`)