Skip to content

Commit

Permalink
looks up safeTx on gnosis tx service api
Browse files Browse the repository at this point in the history
waits for tx to become available
detects contract wallets
uses safeDecodeLogs
drops custom gh next config

Signed-off-by: stadolf <[email protected]>
  • Loading branch information
elmariachi111 committed Oct 9, 2023
1 parent 5502fe9 commit 15bae8e
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 32 deletions.
21 changes: 11 additions & 10 deletions .github/workflows/nextjs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ jobs:
with:
node-version: "16"
cache: ${{ steps.detect-package-manager.outputs.manager }}
#- name: Setup Pages
# uses: actions/configure-pages@v3
# with:
# Automatically inject basePath in your Next.js configuration file and disable
# server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized).
#
# You may remove this line if you want to manage the configuration yourself.
#static_site_generator: next
- name: Setup Pages
uses: actions/configure-pages@v3
with:
# Automatically inject basePath in your Next.js configuration file and disable
# server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized).

# You may remove this line if you want to manage the configuration yourself.
static_site_generator: next
- name: Restore cache
uses: actions/cache@v3
with:
Expand All @@ -79,8 +79,9 @@ jobs:
NEXT_PUBLIC_WC_PROJECT_ID: ${{ secrets.NEXT_PUBLIC_WC_PROJECT_ID }}
NEXT_PUBLIC_INFURA_API_KEY: ${{ secrets.NEXT_PUBLIC_INFURA_API_KEY }}
run: ${{ steps.detect-package-manager.outputs.runner }} next build
#- name: Static HTML export with Next.js
# run: ${{ steps.detect-package-manager.outputs.runner }} next export
# # "next export" is no longer needed when "output: export" is configured in next.config.js
#- name: Static HTML export with Next.js
# run: ${{ steps.detect-package-manager.outputs.runner }} next export
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
with:
Expand Down
1 change: 0 additions & 1 deletion next.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "export",
basePath: "/test-wagmi-safe-privy",
};

module.exports = nextConfig;
56 changes: 39 additions & 17 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,38 @@
"use client";

import { useIsContractWallet } from "@/components/hooks/isContractWallet";
import {
storageABI,
useStorageRetrieve,
useStorageStore,
storageABI,
} from "@/generated/wagmi";
import { Button, Flex, FormControl, Input, Text } from "@chakra-ui/react";
import { resolveSafeTx } from "@/utils/safe";
import { safeDecodeLogs } from "@/utils/safeDecodeLogs";
import {
Button,
Flex,
FormControl,
Input,
Text,
useToast,
} from "@chakra-ui/react";
import { usePrivyWagmi } from "@privy-io/wagmi-connector";
import { useCallback, useEffect, useState } from "react";
import { decodeEventLog } from "viem";
import { useWaitForTransaction } from "wagmi";
import { useNetwork, useWaitForTransaction } from "wagmi";
import { WriteContractResult } from "wagmi/actions";

export default function Home() {
const { ready, wallet } = usePrivyWagmi();
const { ready, wallet: activeWallet, setActiveWallet } = usePrivyWagmi();
const { chain } = useNetwork();

const toast = useToast();
const [newVal, setNewVal] = useState<number>();
const [curVal, setCurVal] = useState<number>();
const [tx, setTx] = useState<WriteContractResult>();

const isContractWallet = useIsContractWallet();

const { data, error, status } = useStorageRetrieve();
const { writeAsync } = useStorageStore();
const { data: receipt, isError, isLoading } = useWaitForTransaction(tx);
Expand All @@ -31,38 +46,45 @@ export default function Home() {
useEffect(() => {
if (!receipt) return;

const numberChangedEvent = receipt.logs
.map((log) =>
decodeEventLog({
abi: storageABI,
...log,
})
)
.find((e) => (e.eventName = "NumberChanged"));
const numberChangedEvent = safeDecodeLogs(receipt, storageABI).find(
(e) => e?.eventName == "NumberChanged"
);
if (!numberChangedEvent) {
console.warn("couldnt find numberchanged event");
return;
}

console.log(numberChangedEvent);
toast({
status: "success",
title: "Number updated",
description: `to ${numberChangedEvent.args._new}`,
});
setCurVal(Number(numberChangedEvent.args._new));
}, [receipt]);
}, [receipt, toast]);

const onSubmit = useCallback(async () => {
if (newVal === undefined) return;
if (!activeWallet || !chain || newVal === undefined) return;

try {
const writeResult = await writeAsync({
args: [BigInt(newVal || 0n)],
});
console.info(writeResult);
setTx(writeResult);
if (isContractWallet) {
//try to resolve the underlying transaction
const resolvedTx = await resolveSafeTx(chain.id, writeResult.hash);
if (!resolvedTx) throw new Error("couldn resolve safe tx");
setTx({ hash: resolvedTx });
} else {
setTx(writeResult);
}
} catch (e: any) {
console.error(e);
}
}, [newVal, writeAsync]);
}, [activeWallet, chain, newVal, writeAsync, isContractWallet]);

if (!wallet) return <Text>Pls connect</Text>;
if (!activeWallet) return <Text>Pls connect</Text>;
return (
<main>
<Text>
Expand Down
8 changes: 4 additions & 4 deletions src/components/LoginButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,23 @@ import { usePrivyWagmi } from "@privy-io/wagmi-connector";
import { useDisconnect } from "wagmi";

export const LoginButton = () => {
const { wallet } = usePrivyWagmi();
const { wallet: activeWallet } = usePrivyWagmi();
const { connectWallet, ready, authenticated, logout } = usePrivy();

const { disconnect } = useDisconnect();

//const { wallets } = useWallets();

if (wallet) {
if (activeWallet) {
return (
<Flex>
<Text>{wallet.address}</Text>
<Text>{activeWallet.address}</Text>
<Button
onClick={() => {
disconnect();
try {
logout();
wallet.disconnect();
activeWallet.disconnect();
} catch (e: any) {
console.warn("cant disconnect wallet");
}
Expand Down
26 changes: 26 additions & 0 deletions src/components/hooks/isContractWallet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { usePrivyWagmi } from "@privy-io/wagmi-connector";
import { useEffect, useState } from "react";
import { Address } from "viem";
import { usePublicClient } from "wagmi";

export const useIsContractWallet = () => {
const publicClient = usePublicClient();
const [isContractWallet, setIsContractWallet] = useState<boolean>();
const { wallet: activeWallet } = usePrivyWagmi();

useEffect(() => {
if (!activeWallet || !publicClient) return;

console.log(activeWallet, publicClient);

//maybe check -> if (activeWallet.connectorType.startsWith("wallet_connect"))

publicClient
.getBytecode({ address: activeWallet.address as Address })
.then((b) => {
setIsContractWallet((b?.length || 0) > 0);
});
}, [activeWallet, publicClient]);

return isContractWallet;
};
5 changes: 5 additions & 0 deletions src/utils/delay.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function delay(milliseconds: number) {
return new Promise((resolve) => {
setTimeout(resolve, milliseconds);
});
}
59 changes: 59 additions & 0 deletions src/utils/safe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// emulates some features of api kit https://github.com/safe-global/safe-core-sdk/tree/main/packages/api-kit
// https://docs.safe.global/safe-core-api/available-services

import { Address } from "viem";
import { goerli, mainnet } from "viem/chains";
import { delay } from "./delay";

type TxServiceApiTransactionResponse = {
safe: Address;
to: Address;
data: `0x${string}`;
blockNumber: number;
transactionHash: `0x${string}`;
safeTxHash: `0x${string}`;
executor: Address;
isExecuted: boolean;
isSuccessful: boolean;
confirmations: Array<{
owner: Address;
}>;
};

const NetworkMap: Record<number, string | null> = {
[goerli.id]: "goerli",
[mainnet.id]: "mainnet",
};

// https://safe-transaction-goerli.safe.global/
// https://safe-transaction-mainnet.safe.global/
/**
* @param network goerli|mainnet
* @param safeTxHash the "internal" safe tx hash
* @returns 0xstring the executed transaction
*/
export const resolveSafeTx = async (
networkId: number,
safeTxHash: `0x${string}`,
attempt = 1
): Promise<`0x${string}` | undefined> => {
const server = `https://safe-transaction-${NetworkMap[networkId]}.safe.global`;
// https://safe-transaction-goerli.safe.global/api/v1/multisig-transactions/0xc02ba93a6f025e3e78dfceb5c9d4d681aa9aafc780ba6243d3d70ac9fdf48288/'
const url = `${server}/api/v1/multisig-transactions/${safeTxHash}`;

const response = <TxServiceApiTransactionResponse>(
await (await fetch(url)).json()
);
console.debug(`Safe Tx Api attempt ${attempt}`, response);
if (response.isSuccessful === null) {
await delay(2500 * attempt);
return resolveSafeTx(networkId, safeTxHash, attempt + 1);
}

if (!response.isSuccessful) {
return undefined;
}
return response.transactionHash;
};

// export const isSafe = (address: Address) => {};
17 changes: 17 additions & 0 deletions src/utils/safeDecodeLogs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Abi, TransactionReceipt, decodeEventLog } from "viem";

export const safeDecodeLogs = (receipt: TransactionReceipt, abi: Abi) => {
return receipt.logs
.map((log) => {
try {
return decodeEventLog({
abi,
data: log.data,
topics: log.topics,
}) as any;
} catch (e) {
return null;
}
})
.filter((log) => !!log);
};

0 comments on commit 15bae8e

Please sign in to comment.