import ArgentCompiledContract from "!!raw-loader!../../contracts/ArgentAccount.txt" ; import { ethers } from "ethers" ; import { Provider , compileCalldata , ec , stark } from "starknet" ; import browser from "webextension-polyfill" ; import { BackupWallet } from "../../shared/backup.model" ; import { Network , getProvider } from "../../shared/networks" ; import { selectedWalletStore } from "../selectedWallet" ; import { Storage } from "../storage" ; const isDev = process . env . NODE_ENV === "development" ; interface StorageProps { encKeystore ? : string ; wallets : BackupWallet [ ] ; } const store = new Storage < StorageProps > ( { wallets : [ ] } , "L1" ) ; export async function existsL1 ( ) { return Boolean ( await store . getItem ( "encKeystore" ) ) ; } export async function validatePassword ( password ) { try { await getL1 ( password ) ; return true ; } catch ( e ) { console . error ( e ) ; return false ; } } let rawWalletTimeoutPid ; let rawWallet ; function setRawWallet ( wallet ) { rawWallet = wallet ; rawWalletTimeoutPid = setTimeout ( ( ) => { rawWallet = undefined ; } , 15 * 60 * 60 * 1000 ) as unknown as number ; } export function isUnlocked ( ) { return Boolean ( rawWallet ) ; } export async function setKeystore ( keystore ) { setRawWallet ( undefined ) ; await store . setItem ( "encKeystore" , keystore ) ; } async function recoverL1 ( password , progressFn = ( ) => { } ) { if ( ! ( await existsL1 ( ) ) ) { throw Error ( "No KeyPair exists" ) ; } const encKeyPair = await store . getItem ( "encKeystore" ) ; return await ethers . Wallet . fromEncryptedJson ( encKeyPair ! , password , progressFn ) ; } async function generateL1 ( ) { if ( await existsL1 ( ) ) { throw Error ( "KeyPair already exists" ) ; } return ethers . Wallet . createRandom ( ) ; } let sessionPassword ; let recoverPromise ; export async function getL1 ( password ? ) { if ( rawWallet ) { return rawWallet ; } else if ( await existsL1 ( ) ) { if ( ! recoverPromise ) { if ( ! password ) { throw Error ( "Password required" ) ; } recoverPromise = recoverL1 ( password ) ; } const recoveredWallet = await recoverPromise ; setRawWallet ( recoveredWallet ) ; sessionPassword = password ; const encKeyPair = JSON . parse ( ( await store . getItem ( "encKeystore" ) ) || "{}" ) ; store . setItem ( "wallets" , encKeyPair . wallets ?? [ ] ) ; if ( ( await selectedWalletStore . getItem ( "SELECTED_WALLET" ) ) . address === "" && encKeyPair . wallets . length > 0 ) { await selectedWalletStore . setItem ( "SELECTED_WALLET" , encKeyPair . wallets [ 0 ] ) ; } return recoveredWallet ; } else { sessionPassword = password ; const wallet = await generateL1 ( ) ; setRawWallet ( wallet ) ; return wallet ; } } export function lockWallet ( ) { setRawWallet ( undefined ) ; recoverPromise = undefined ; sessionPassword = undefined ; } async function getEncKeyStore ( wallet , password , wallets , progressFn = ( ) => { } ) { const backup = await wallet . encrypt ( password , { scrypt : { N : isDev ? 64 : 32768 , } , } , progressFn ) ; const extendedBackup = JSON . stringify ( { ... JSON . parse ( backup ) , wallets , } , null , 2 ) ; return extendedBackup ; } function downloadTextFile ( text , filename ) { const blob = new Blob ( [ text ] , { type : "application/json" , } ) ; const url = URL . createObjectURL ( blob ) ; browser . downloads . download ( { url , filename , } ) ; } export const getWallets = async ( ) : Promise < BackupWallet [ ] > => await store . getItem ( "wallets" ) ; export async function createAccount ( networkId ) { if ( ! sessionPassword ) { throw Error ( "Password required" ) ; } const l1 = await getL1 ( ) ; const starkPair = ec . getKeyPair ( l1 . privateKey ) ; const starkPub = ec . getStarkKey ( starkPair ) ; const seed = ec . getStarkKey ( ec . genKeyPair ( ) ) ; const wallets = await getWallets ( ) ; const provider = getProvider ( networkId ) ; const deployTransaction = await provider . deployContract ( ArgentCompiledContract , compileCalldata ( { signer : starkPub , guardian : "0" , } ) , seed ) ; if ( deployTransaction . code !== "TRANSACTION_RECEIVED" ) { throw new Error ( "Deploy transaction failed" ) ; } const newWallet = { network : networkId , address : deployTransaction . address ! } ; const newWallets = [ ... wallets , newWallet ] ; const encKeyStore = await getEncKeyStore ( l1 , sessionPassword , newWallets ) ; store . setItem ( "encKeystore" , encKeyStore ) ; store . setItem ( "wallets" , newWallets ) ; downloadTextFile ( encKeyStore , "starknet-backup.json" ) ; return { address : deployTransaction . address ! , txHash : deployTransaction . transaction_hash , wallets , } ; } export async function downloadBackupFile ( ) { const encKeyStore = ( await store . getItem ( "encKeystore" ) ) ?? "" ; downloadTextFile ( encKeyStore , "starknet-backup.json" ) ; } export async function resetAll ( ) { lockWallet ( ) ; await browser . storage . local . clear ( ) ; }