diff --git a/.changeset/tasty-masks-kiss.md b/.changeset/tasty-masks-kiss.md index 935ee122..c33f8c5c 100644 --- a/.changeset/tasty-masks-kiss.md +++ b/.changeset/tasty-masks-kiss.md @@ -2,4 +2,4 @@ "@burnt-labs/abstraxion": minor --- -Add grantee signer client to seamlessly handle grantor/grantee relationships +Add grantee signer client to seamlessly handle granter/grantee relationships diff --git a/.changeset/tough-dolls-count.md b/.changeset/tough-dolls-count.md new file mode 100644 index 00000000..60dca95e --- /dev/null +++ b/.changeset/tough-dolls-count.md @@ -0,0 +1,5 @@ +--- +"@burnt-labs/abstraxion": minor +--- + +persistence and related state cleanup diff --git a/packages/abstraxion/CHANGELOG.md b/packages/abstraxion/CHANGELOG.md index a5a5c65a..0533a6f8 100644 --- a/packages/abstraxion/CHANGELOG.md +++ b/packages/abstraxion/CHANGELOG.md @@ -6,7 +6,7 @@ - [#41](https://github.com/burnt-labs/xion.js/pull/41) [`a269cdf`](https://github.com/burnt-labs/xion.js/commit/a269cdf88722408e91b643d12ce4181ce26296f3) Thanks [@BurntVal](https://github.com/BurntVal)! - abstraxion dynamic url for grant creation on dashboard -- [#44](https://github.com/burnt-labs/xion.js/pull/44) [`56b9f87`](https://github.com/burnt-labs/xion.js/commit/56b9f87482a7210072eaa279960d1ff01ad5b4e0) Thanks [@justinbarry](https://github.com/justinbarry)! - Add grantee signer client to seamlessly handle grantor/grantee relationships +- [#44](https://github.com/burnt-labs/xion.js/pull/44) [`56b9f87`](https://github.com/burnt-labs/xion.js/commit/56b9f87482a7210072eaa279960d1ff01ad5b4e0) Thanks [@justinbarry](https://github.com/justinbarry)! - Add grantee signer client to seamlessly handle granter/grantee relationships ### Patch Changes diff --git a/packages/abstraxion/src/GranteeSignerClient.ts b/packages/abstraxion/src/GranteeSignerClient.ts index 6165c4bf..bd404c09 100644 --- a/packages/abstraxion/src/GranteeSignerClient.ts +++ b/packages/abstraxion/src/GranteeSignerClient.ts @@ -14,12 +14,12 @@ import { } from "@cosmjs/tendermint-rpc"; interface GranteeSignerOptions { - readonly grantorAddress: string; + readonly granterAddress: string; readonly granteeAddress: string; } export class GranteeSignerClient extends SigningCosmWasmClient { - protected readonly grantorAddress: string; + protected readonly granterAddress: string; protected readonly granteeAddress: string; public static async connectWithSigner( @@ -43,16 +43,16 @@ export class GranteeSignerClient extends SigningCosmWasmClient { cometClient: TendermintClient | undefined, signer: OfflineSigner, { - grantorAddress, + granterAddress, granteeAddress, ...options }: SigningCosmWasmClientOptions & GranteeSignerOptions, ) { super(cometClient, signer, options); - if (grantorAddress === undefined) { - throw new Error("grantorAddress is required"); + if (granterAddress === undefined) { + throw new Error("granterAddress is required"); } - this.grantorAddress = grantorAddress; + this.granterAddress = granterAddress; if (granteeAddress === undefined) { throw new Error("granteeAddress is required"); @@ -66,8 +66,8 @@ export class GranteeSignerClient extends SigningCosmWasmClient { fee: StdFee | "auto" | number, memo = "", ): Promise { - // Figure out if the signerAddress is a grantor - if (signerAddress === this.grantorAddress) { + // Figure out if the signerAddress is a granter + if (signerAddress === this.granterAddress) { signerAddress = this.granteeAddress; // Wrap the signerAddress in a MsgExec messages = [ @@ -91,8 +91,8 @@ export class GranteeSignerClient extends SigningCosmWasmClient { memo: string, explicitSignerData?: SignerData, ): Promise { - // Figure out if the signerAddress is a grantor - if (signerAddress === this.grantorAddress) { + // Figure out if the signerAddress is a granter + if (signerAddress === this.granterAddress) { signerAddress = this.granteeAddress; // Wrap the signerAddress in a MsgExec messages = [ diff --git a/packages/abstraxion/src/components/Abstraxion/index.tsx b/packages/abstraxion/src/components/Abstraxion/index.tsx index 694ff456..725197c4 100644 --- a/packages/abstraxion/src/components/Abstraxion/index.tsx +++ b/packages/abstraxion/src/components/Abstraxion/index.tsx @@ -42,7 +42,7 @@ export function Abstraxion({ ) : isConnecting ? ( ) : isConnected ? ( - + ) : ( )} diff --git a/packages/abstraxion/src/components/AbstraxionContext/index.tsx b/packages/abstraxion/src/components/AbstraxionContext/index.tsx index 6222f976..94cfd154 100644 --- a/packages/abstraxion/src/components/AbstraxionContext/index.tsx +++ b/packages/abstraxion/src/components/AbstraxionContext/index.tsx @@ -10,8 +10,8 @@ export interface AbstraxionContextProps { setAbstraxionError: React.Dispatch>; abstraxionAccount: DirectSecp256k1HdWallet | undefined; setAbstraxionAccount: React.Dispatch; - grantorAddress: string; - setGrantorAddress: React.Dispatch>; + granterAddress: string; + setgranterAddress: React.Dispatch>; contracts?: string[]; dashboardUrl?: string; } @@ -35,7 +35,7 @@ export const AbstraxionContextProvider = ({ const [abstraxionAccount, setAbstraxionAccount] = useState< DirectSecp256k1HdWallet | undefined >(undefined); - const [grantorAddress, setGrantorAddress] = useState(""); + const [granterAddress, setgranterAddress] = useState(""); return ( { - if (!keypair) { - throw new Error("No keypair"); + async function pollForGrants(address: string): Promise { + if (!address) { + throw new Error("No keypair address"); } - setIsConnecting(true); - const accounts = await keypair.getAccounts(); - const address = accounts[0].address; const shouldContinue = true; while (shouldContinue) { try { @@ -95,41 +96,46 @@ export function AbstraxionSignin(): JSX.Element { ); const data = (await res.json()) as GrantsResponse; if (data.grants?.length > 0) { + setIsConnecting(true); const granterAddresses = data.grants.map((grant) => grant.granter); const uniqueGranters = [...new Set(granterAddresses)]; if (uniqueGranters.length > 1) { console.error("More than one granter found. Taking first."); } - setGrantorAddress(uniqueGranters[0]); + configuregranter(uniqueGranters[0]); break; } } catch (error) { - console.log("There was an error polling for grants: ", error); + throw error; } } - - setIsConnecting(false); - setIsConnected(true); - setAbstraxionAccount(keypair); } useEffect(() => { async function onStartup() { - const existingKeypair = localStorage.getItem("xion-authz-temp-account"); - let keypair; - if (existingKeypair) { - keypair = await DirectSecp256k1HdWallet.deserialize( - existingKeypair, - "abstraxion", - ); - } else { - keypair = await generateAndStoreTempAccount(); + try { + const existingKeypair = localStorage.getItem("xion-authz-temp-account"); + let keypair; + if (existingKeypair) { + keypair = await DirectSecp256k1HdWallet.deserialize( + existingKeypair, + "abstraxion", + ); + } else { + keypair = await generateAndStoreTempAccount(); + } + const accounts = await keypair.getAccounts(); + const address = accounts[0].address; + setTempAccountAddress(address); + openDashboardTab(address, contracts); + await pollForGrants(address); + setIsConnecting(false); + setIsConnected(true); + setAbstraxionAccount(keypair); + } catch (error) { + console.log("Something went wrong: ", error); } - const accounts = await keypair.getAccounts(); - const address = accounts[0].address; - openDashboardTab(address, contracts); - pollForGrants(keypair); } if (!isMounted.current) { @@ -151,7 +157,12 @@ export function AbstraxionSignin(): JSX.Element { - + ); } diff --git a/packages/abstraxion/src/components/Connected/Connected.tsx b/packages/abstraxion/src/components/Connected/Connected.tsx index 75a13d64..6b9e5b0b 100644 --- a/packages/abstraxion/src/components/Connected/Connected.tsx +++ b/packages/abstraxion/src/components/Connected/Connected.tsx @@ -5,14 +5,17 @@ import { AbstraxionContextProps, } from "../AbstraxionContext"; -export function Connected() { - const { setIsConnected } = useContext( - AbstraxionContext, - ) as AbstraxionContextProps; +export function Connected({ onClose }: { onClose: VoidFunction }) { + const { setIsConnected, setAbstraxionAccount, setgranterAddress } = + useContext(AbstraxionContext) as AbstraxionContextProps; function handleLogout() { - localStorage.removeItem("xion-authz-temp-account"); setIsConnected(false); + localStorage.removeItem("xion-authz-temp-account"); + localStorage.removeItem("xion-authz-granter-account"); + setAbstraxionAccount(undefined); + setgranterAddress(""); + onClose(); } return ( diff --git a/packages/abstraxion/src/hooks/useAbstraxionAccount.ts b/packages/abstraxion/src/hooks/useAbstraxionAccount.ts index e94d1c3e..9d914de0 100644 --- a/packages/abstraxion/src/hooks/useAbstraxionAccount.ts +++ b/packages/abstraxion/src/hooks/useAbstraxionAccount.ts @@ -1,8 +1,9 @@ -import { useContext } from "react"; +import { useContext, useEffect } from "react"; import { AbstraxionContext, AbstraxionContextProps, } from "@/src/components/AbstraxionContext"; +import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; export interface AbstraxionAccount { bech32Address: string; @@ -14,13 +15,51 @@ export interface useAbstraxionAccountProps { } export const useAbstraxionAccount = (): useAbstraxionAccountProps => { - const { isConnected, grantorAddress } = useContext( - AbstraxionContext, - ) as AbstraxionContextProps; + const { + isConnected, + granterAddress, + abstraxionAccount, + isConnecting, + setgranterAddress, + setAbstraxionAccount, + setIsConnected, + setIsConnecting, + } = useContext(AbstraxionContext) as AbstraxionContextProps; + + useEffect(() => { + async function configureAccount() { + setIsConnecting(true); + const tempKeypair = localStorage.getItem("xion-authz-temp-account"); + if (tempKeypair) { + const deserializedKeypair = await DirectSecp256k1HdWallet.deserialize( + tempKeypair, + "abstraxion", + ); + setAbstraxionAccount(deserializedKeypair); + const granterAccount = localStorage.getItem( + "xion-authz-granter-account", + ); + if (granterAccount) { + setgranterAddress(granterAccount); + setIsConnected(true); + } + } else { + // Wipe granter even if it exists, clean context + localStorage.removeItem("xion-authz-granter-account"); + setAbstraxionAccount(undefined); + setgranterAddress(""); + } + setIsConnecting(false); + } + + if (!isConnecting && !abstraxionAccount && !granterAddress) { + configureAccount(); + } + }, [isConnected, abstraxionAccount, granterAddress]); return { data: { - bech32Address: grantorAddress, + bech32Address: granterAddress, }, isConnected: isConnected, }; diff --git a/packages/abstraxion/src/hooks/useAbstraxionSigningClient.ts b/packages/abstraxion/src/hooks/useAbstraxionSigningClient.ts index cc5b290e..e337435f 100644 --- a/packages/abstraxion/src/hooks/useAbstraxionSigningClient.ts +++ b/packages/abstraxion/src/hooks/useAbstraxionSigningClient.ts @@ -8,7 +8,7 @@ import { import { GranteeSignerClient } from "@/src/GranteeSignerClient.ts"; export const useAbstraxionSigningClient = () => { - const { isConnected, abstraxionAccount, grantorAddress } = useContext( + const { isConnected, abstraxionAccount, granterAddress } = useContext( AbstraxionContext, ) as AbstraxionContextProps; @@ -22,6 +22,10 @@ export const useAbstraxionSigningClient = () => { if (!abstraxionAccount) { throw new Error("No account found."); } + + if (!granterAddress) { + throw new Error("No granter found."); + } const granteeAddress = await abstraxionAccount .getAccounts() .then((accounts) => { @@ -36,7 +40,7 @@ export const useAbstraxionSigningClient = () => { abstraxionAccount, { gasPrice: GasPrice.fromString("0uxion"), - grantorAddress, + granterAddress, granteeAddress, }, ); @@ -44,13 +48,12 @@ export const useAbstraxionSigningClient = () => { setAbstractClient(directClient); } catch (error) { console.log("Something went wrong: ", error); + setAbstractClient(undefined); } } - if (isConnected && abstraxionAccount) { - getSigner(); - } - }, [abstraxionAccount, isConnected]); + getSigner(); + }, [isConnected, abstraxionAccount, granterAddress]); return { client: abstractClient }; };