useAuth.ts
1 import { useCallback, useEffect, useState } from "react"; 2 import { Actor, ActorSubclass, HttpAgent, Identity } from "@dfinity/agent" 3 import { AuthClient } from "@dfinity/auth-client"; 4 import { CkBTCMinterCanister } from "@dfinity/ckbtc"; 5 import { Principal } from "@dfinity/principal"; 6 import { IcrcLedgerCanister, IcrcTokenMetadataResponse } from "@dfinity/ledger-icrc"; 7 import { 8 idlFactory as repositoryIdl 9 } from "../../declarations/repository"; 10 import { _SERVICE as _REPOSITORY_SERVICE } from "@/declarations/repository/repository.did"; 11 12 13 interface AuthHooks { 14 isAuthenticating: boolean; 15 authClient: AuthClient | null; 16 isAuthenticated: boolean; 17 identity: Identity | Promise<Identity> | undefined; 18 principal: string | undefined; 19 agent: HttpAgent | undefined; 20 hasLoggedIn: boolean; 21 repositoryActor: ActorSubclass<_REPOSITORY_SERVICE | null> 22 login: () => Promise<void>; 23 logout: () => void; 24 minterActor: CkBTCMinterCanister | undefined; 25 btcAddress: string | undefined; 26 ledgerActor: IcrcLedgerCanister | undefined; 27 metadata: IcrcTokenMetadataResponse | undefined; 28 balance: bigint | null | undefined; 29 } 30 31 32 const useAuth = (): AuthHooks => { 33 const [isAuthenticating, setIsAuthenticating] = useState<boolean>(false); 34 const [authClient, setAuthClient] = useState<AuthClient | null>(null); 35 const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false); 36 const [identity, setIdentity] = useState<Identity | Promise<Identity> | undefined>(); 37 const [principal, setPrincipal] = useState<string | undefined>(); 38 const [hasLoggedIn, setHasLoggedIn] = useState<boolean>(false); 39 const [repositoryActor, setRepositoryActor] = useState(null); 40 const [walletPrincipal, setWalletPrincipal] = useState(null); 41 const [walletCanister, setWalletCanister] = useState(null); 42 const [minterActor, setMinterActor] = useState<CkBTCMinterCanister | undefined>(); 43 const [btcAddress, setBtcAddress] = useState<string | undefined>(); 44 const [ledgerActor, setLedgerActor] = useState<IcrcLedgerCanister | undefined>(); 45 const [metadata, setMetadata] = useState<IcrcTokenMetadataResponse | undefined>(); 46 const [balance, setBalance] = useState<bigint | null | undefined>(); 47 const network = process.env.DFX_NETWORK || "local"; 48 const host = "https://icp0.io"; 49 const localhost = "http://localhost:4943"; 50 const repositoryCanisterId = process.env.CANISTER_ID_REPOSITORY; 51 const minterCanisterId = process.env.MY_CKBTC_MINTER_CANISTER_ID; 52 const ledgerCanisterId = process.env.MY_LEDGER_CANISTER_ID; 53 54 const login = useCallback(async () => { 55 const alreadyAuthenticated = await authClient?.isAuthenticated() 56 console.log("alreadyAuthenticated: ", alreadyAuthenticated); 57 58 if (alreadyAuthenticated) { 59 setIsAuthenticated(true); 60 setHasLoggedIn(true); 61 } else { 62 setIsAuthenticating(true) 63 64 authClient?.login({ 65 identityProvider: process.env.DFX_NETWORK === "ic" 66 ? "https://identity.ic0.app/#authorize" 67 : "http://localhost:4943?canisterId=bkyz2-fmaaa-aaaaa-qaaaq-cai", 68 onSuccess: async () => { 69 setIsAuthenticating(false); 70 setIsAuthenticated(true); 71 setHasLoggedIn(true); 72 73 } 74 }) 75 } 76 77 },[authClient]); 78 79 const logout = () => { 80 setIsAuthenticated(false); 81 82 authClient?.logout({ returnTo: "/home_page" }); 83 }; 84 85 86 let agent: HttpAgent | undefined; 87 const createAgent = () => { 88 agent = new HttpAgent({ 89 host: network === "local" ? localhost : host, 90 identity: identity, 91 }); 92 93 console.log("Agent: ", agent); 94 95 if (network === "local") { 96 agent.fetchRootKey(); 97 } 98 } 99 100 const createRepositoryActor = async () => { 101 console.log("Repository canister id: ", repositoryCanisterId); 102 const repositoryActor = Actor.createActor(repositoryIdl, { 103 agent, 104 canisterId: Principal.fromText(repositoryCanisterId), 105 }); 106 console.log("Repository canister: ", repositoryActor); 107 setRepositoryActor(repositoryActor); 108 } 109 110 const createMinterActor = async () => { 111 console.log("Minter canister Id: ", minterCanisterId); 112 if(!identity || !agent) return; 113 const minterActor = CkBTCMinterCanister.create({ 114 agent, 115 canisterId: Principal.fromText(minterCanisterId), 116 }); 117 console.log("Minter actor: ", minterActor); 118 setMinterActor(minterActor); 119 if(minterActor) { 120 getBtcAddress(); 121 } 122 }; 123 124 const getBtcAddress = async () => { 125 if(!minterActor || !identity) return; 126 try { 127 const btcAddress = await minterActor?.getBtcAddress({}); 128 setBtcAddress(btcAddress); 129 console.log("Send Btc to this address to get ckBtc: ", btcAddress); 130 } catch(error) { 131 console.log("There was an issue with fetching the Btc address: ", error); 132 } 133 }; 134 135 const createLedgerActor = async () => { 136 console.log("Ledger canister Id: ", ledgerCanisterId); 137 if(identity && agent ) { 138 const ledgerActor = IcrcLedgerCanister.create({ 139 agent, 140 canisterId: Principal.fromText(ledgerCanisterId), 141 }) 142 console.log("Ledger actor: ", ledgerActor); 143 setLedgerActor(ledgerActor); 144 await getMetadata(); 145 getBalance(); 146 } else { 147 console.log("Idetity or agent not initialized"); 148 } 149 150 } 151 152 const getMetadata = async ()=> { 153 if(ledgerActor) { 154 try { 155 const res = await ledgerActor?.metadata({ certified: false }) 156 console.log("Meta data: ", res) 157 setMetadata(res); 158 } catch(error) { 159 console.log("There was an isue with fethcing the metadata: ", error); 160 } 161 162 } else { 163 console.log("Ledger actor not initialized"); 164 } 165 }; 166 167 const getBalance = async () => { 168 if(ledgerActor && principal ) { 169 const ownerPrincipal = Principal.fromText(principal); 170 console.log("Owner principal: ", ownerPrincipal); 171 try { 172 const res = await ledgerActor?.balance({ 173 owner: ownerPrincipal, 174 certified: false, 175 }); 176 setBalance(res); 177 } catch(error) { 178 console.log("There was an issue fethcing the balance: ", error); 179 } 180 } else { 181 console.log("Make sure ledgerActor and principal are intitialized first"); 182 } 183 } 184 185 useEffect(() => { 186 if (authClient == null) { 187 setIsAuthenticating(true); 188 189 AuthClient.create().then(async (client) => { 190 await client?.isAuthenticated(); 191 setIsAuthenticating(false); 192 setAuthClient(client); 193 }); 194 } 195 }, [authClient]); 196 197 useEffect(() => { 198 if (authClient && login != null) { 199 (async () => { 200 const authenticated = await authClient?.isAuthenticated(); 201 console.log("Authenticated: ", authenticated); 202 if (authenticated) { 203 setIsAuthenticated(true); 204 } 205 })(); 206 } 207 }, [authClient]); 208 209 useEffect(() => { 210 if (isAuthenticated) { 211 const identity = authClient.getIdentity(); 212 setIdentity(identity); 213 const principal = identity.getPrincipal().toString(); 214 setPrincipal(principal); 215 216 console.log("Identity: ", identity); 217 console.log("Principal: ", principal); 218 219 createAgent() 220 createRepositoryActor(); 221 createMinterActor(); 222 createLedgerActor(); 223 } 224 }, [isAuthenticated]); 225 226 return { 227 authClient, 228 isAuthenticated, 229 hasLoggedIn, 230 isAuthenticating, 231 login, 232 logout, 233 identity, 234 principal, 235 agent, 236 repositoryActor, 237 minterActor, 238 btcAddress, 239 ledgerActor, 240 metadata, 241 balance, 242 }; 243 }; 244 245 export default useAuth;