UNPKG

@ixily/activ

Version:

Alpha Capture Trade Idea Verification. Blockchain ownership proven trade ideas and strategies.

485 lines (444 loc) 13.9 kB
import { BehaviorSubject } from 'rxjs' import { LogModule as log } from '../../modules/chunks/log.module' import { CHAIN_IDS, Contract, ContractInterface, CONTRACTS, ethers, IBlockchainNetwork, IChain, IContract, IContractRecipe, isNullOrUndefined, IWeb3AuthorizationOptions, makeSureChainMatches, SUPPORTED_BLOCKCHAIN_NETWORKS, } from '../..' import { BigNumber } from 'ethers' import { WALLET_NETWORK_CHAIN_NAME } from '../../interfaces/chunks/blockchain-network.i' const state = { blockchainNetworks: new Map<string, IBlockchainNetwork>(), selectedBlockchainNetwork: undefined as string | undefined, selectedContract: undefined as string | undefined, chainId: 0 as number, gateContract: undefined as Contract | undefined, viewsContract: undefined as Contract | undefined, updated$: new BehaviorSubject<boolean>(false), recordCreds: undefined as undefined | IWeb3AuthorizationOptions, browserRecordedWallets: [] as string[], browserSelectedWallet: undefined as string | undefined, accountsChangedListener: undefined as any | undefined, remover: undefined as any | undefined, } const getUpdatedStream = (): BehaviorSubject<boolean> => { return state.updated$ } const changeToContract = async ( blockchainNetwork: string, contractName: string | IContractRecipe, details?: IWeb3AuthorizationOptions, ): Promise<IContract> => { if (details !== undefined) { state.recordCreds = details } if (details === undefined && state.recordCreds !== undefined) { details = state.recordCreds } if (details === undefined) { throw new Error( 'Contract Module Error: Details is undefined. You must provide details on how to build contract by the first time you call it.', ) } if ( details?.userWalletPrivateKey === undefined && details?.webProvider === undefined ) { throw new Error( 'Contract Module Error: You must provide either a web provider or a private key', ) } const bn = (await SUPPORTED_BLOCKCHAIN_NETWORKS())[blockchainNetwork] if (isNullOrUndefined(bn)) { throw new Error(`Blockchain network ${blockchainNetwork} not found`) } if (typeof contractName === 'string') { if (isNullOrUndefined(bn.contracts[contractName])) { throw new Error(`Contract ${contractName} not found`) } } let alreadyContract = state.blockchainNetworks.get(blockchainNetwork) if (isNullOrUndefined(alreadyContract)) { state.blockchainNetworks.set(blockchainNetwork, bn) alreadyContract = state.blockchainNetworks.get(blockchainNetwork) } let contract: IContract if (typeof contractName === 'string') { contract = alreadyContract!.contracts[contractName] } else { alreadyContract!.contracts[contractName.nameOrVersion] = { name: contractName.nameOrVersion, recipe: contractName, gate: undefined as Contract | undefined, cacheTax5min: undefined as BigNumber | undefined, cacheTaxLastTime: undefined as number | undefined, } contract = alreadyContract!.contracts[contractName.nameOrVersion] } const { gateContract } = await build( contract.recipe, details!.userWalletPrivateKey, details!.webProvider, ) contract.gate = gateContract // contract.core = coreContract state.selectedBlockchainNetwork = blockchainNetwork state.selectedContract = typeof contractName === 'string' ? contractName : contractName.nameOrVersion state.browserRecordedWallets = [] state.browserSelectedWallet = undefined state.updated$.next(true) return contract } const buildContract = async ( chain: IChain, contractAddress: string, abi: ContractInterface, webProvider?: any, providerUrl?: string, userWalletPrivateKey?: string, ): Promise<Contract> => { let contract: Contract = undefined as any // in this context we have a user wallet connected via web3 provider if (!isNullOrUndefined(webProvider)) { // console.log('chain:') // console.log(chain) state.chainId = CHAIN_IDS(chain) /* if (chain === 'hardhat') { // console.log('hardhatDemoWalletToUse:') // console.log(hardhatDemoWalletToUse) const provider = new ethers.providers.JsonRpcProvider(providerUrl) const wallet = new ethers.Wallet( userWalletPrivateKey as string, provider, ) // when the user open activ from browser but the user have not installed the wallet we need to use the provider directly instead of the signer try { const signer = wallet.connect(provider) contract = new ethers.Contract(contractAddress, abi, signer) } catch (error) { contract = new ethers.Contract(contractAddress, abi, provider) } } else { */ if (isNullOrUndefined(userWalletPrivateKey)) { try { const provider = new ethers.providers.Web3Provider( webProvider, CHAIN_IDS(chain), ) // when the user open activ from browser but the user have not installed the wallet we need to use the provider directly instead of the signer const signer = provider.getSigner() contract = new ethers.Contract(contractAddress, abi, signer) // console.log('contract') // console.log(contract) } catch (error) { const provider = new ethers.providers.JsonRpcProvider( providerUrl, ) await provider.send('eth_requestAccounts', []) const signer = provider.getSigner() contract = new ethers.Contract(contractAddress, abi, signer) // console.log('signer') // console.log(signer) // console.log('contractAddress') // console.log(contractAddress) // throw error } } else { if (webProvider.mockedInTesting) { const wallet = new ethers.Wallet( userWalletPrivateKey as string, webProvider, ) const signer = wallet.connect(webProvider) contract = new ethers.Contract(contractAddress, abi, signer) } else { const provider = new ethers.providers.Web3Provider( webProvider, chain, ) const wallet = new ethers.Wallet( userWalletPrivateKey as string, provider, ) // when the user open activ from browser but the user have not installed the wallet we need to use the provider directly instead of the signer try { const signer = wallet.connect(provider) contract = new ethers.Contract(contractAddress, abi, signer) } catch (error) { contract = new ethers.Contract( contractAddress, abi, provider, ) } } } // } } else if ( // in this context the user there are not a wallet connected isNullOrUndefined(webProvider) && isNullOrUndefined(userWalletPrivateKey) ) { const provider = new ethers.providers.JsonRpcProvider(providerUrl) contract = new ethers.Contract(contractAddress, abi, provider) } else if ( // in this context we have a user wallet connected via private key isNullOrUndefined(webProvider) && !isNullOrUndefined(userWalletPrivateKey) ) { const provider = new ethers.providers.JsonRpcProvider(providerUrl) const wallet = new ethers.Wallet( userWalletPrivateKey as string, provider, ) const signer = wallet.connect(provider) contract = new ethers.Contract(contractAddress, abi, signer) } // await state.provider.request({ // method: 'wallet_switchEthereumChain', // params: [{ chainId }], // }) // console.log('chain:') // console.log(chain) // console.log('contractAddress:') // console.log(contractAddress) // console.log('abi:') // console.log(abi) // console.log('webProvider:') // console.log(webProvider) // console.log('providerUrl:') // console.log(providerUrl) // console.log('userWalletPrivateKey:') // console.log(userWalletPrivateKey) // console.log('----------------') // console.log('----------------') // // console.log('contract:') // console.log(contract) if (contract === undefined) { throw new Error('Could not build Contract') } await makeSureChainMatches(chain) return contract } const build = async ( contractRecipe: IContractRecipe, userWalletPrivateKey?: string, webProvider?: any, ): Promise<{ gateContract: Contract // coreContract: Contract }> => { log.dev('contractRecipe.gateContractAddress') log.dev(contractRecipe.gateContractAddress) const gateContract = await buildContract( contractRecipe.chain, contractRecipe.gateContractAddress, contractRecipe.gateAbi, webProvider, contractRecipe.providerUrl, userWalletPrivateKey, ) // const coreContract = await buildContract( // contractRecipe.chain, // contractRecipe.coreContractAddress, // contractRecipe.coreAbi, // webProvider, // contractRecipe.providerUrl, // userWalletPrivateKey, // ) return { gateContract, // coreContract, } } const listBlockchainNetworks = (): string[] => { const SUPPORTED_BLOCKCHAIN_NETWORK_NAMES: string[] = [] for (const [key, value] of state.blockchainNetworks) { SUPPORTED_BLOCKCHAIN_NETWORK_NAMES.push(value.name) } return SUPPORTED_BLOCKCHAIN_NETWORK_NAMES } const listContractsForNetwork = (network: string): string[] => { const CONTRACT_NAMES: string[] = [] const contracts = state.blockchainNetworks.get(network)!.contracts for (const key in contracts) { CONTRACT_NAMES.push(key) } return CONTRACT_NAMES } const getBlockchainNetwork = (): IBlockchainNetwork => { return state.blockchainNetworks.get(state.selectedBlockchainNetwork!)! } const getBlockchainContract = (): IContract => { // console.log('state.selectedBlockchainNetwork') // console.log(state.selectedBlockchainNetwork) // console.log( // 'state.blockchainNetworks.get(state.selectedBlockchainNetwork!)!', // ) // console.log(state.blockchainNetworks.get(state.selectedBlockchainNetwork!)!) return state.blockchainNetworks.get(state.selectedBlockchainNetwork!)! .contracts[state.selectedContract!]! } const get = (): IContract | undefined => { return state.blockchainNetworks.get(state.selectedBlockchainNetwork!) ?.contracts[state.selectedContract!] } const getGate = (): Contract | undefined => { return state.blockchainNetworks.get(state.selectedBlockchainNetwork!) ?.contracts[state.selectedContract!]?.gate } const thisOrGet = (contract?: IContract): IContract => { if (contract === undefined) { contract = get() } if (contract === undefined) { throw new Error('Contract is undefined') } return contract! } const requestSelectedBrowserWalletAddress = async ( contract?: IContract, ): Promise<string> => { contract = thisOrGet(contract) if (state.browserRecordedWallets.length === 0) { const wp = getRecordedContractCreds()?.webProvider if (wp === undefined) { throw new Error('CONTRACT MODULE ERROR: Web provider is undefined') } if (state.accountsChangedListener !== undefined) { if (state.remover !== undefined) { state.remover() } state.accountsChangedListener = undefined state.remover = undefined } if (state.accountsChangedListener === undefined) { const accounts = await wp.request({ method: 'eth_requestAccounts' }) state.browserRecordedWallets = accounts state.browserSelectedWallet = accounts[0] // console.log('state.browserRecordedWallets') // console.log(state.browserRecordedWallets) // console.log('state.browserSelectedWallet') // console.log(state.browserSelectedWallet) state.accountsChangedListener = wp.on( 'accountsChanged', (accounts: string[]) => { state.browserRecordedWallets = accounts state.browserSelectedWallet = accounts[0] // console.log('state.browserRecordedWallets') // console.log(state.browserRecordedWallets) // console.log('state.browserSelectedWallet') // console.log(state.browserSelectedWallet) }, ) state.remover = () => { wp.removeListener( 'accountsChanged', state.accountsChangedListener, ) } } return state.browserSelectedWallet! } return state.browserSelectedWallet! } const thisOrGetCreator = async ( creator?: string, contract?: IContract, ): Promise<string> => { contract = thisOrGet(contract) if (creator === undefined) { if ( contract.gate?.signer === undefined || contract.gate?.signer === null ) { // probalby browser case creator = await requestSelectedBrowserWalletAddress() } else { // probably node case creator = await contract.gate!.signer.getAddress() } } if (creator === undefined) { throw new Error('Contract signer is undefined') } return creator! } const thisOrGetGate = (contract?: Contract): Contract => { if (contract === undefined) { contract = getGate() } if (contract === undefined) { throw new Error('Contract is undefined') } return contract! } export interface IWalletSettings extends IContractRecipe { callerAddress?: string balanceInEth: string } const getWalletSettings = async ( contract?: IContract, ): Promise<IWalletSettings> => { contract = thisOrGet(contract) const callerAddress = await contract?.gate?.signer?.getAddress() const balance = await contract?.gate?.signer?.getBalance() const balanceInEth = Number( ethers?.utils?.formatEther(balance || 0), ).toFixed(4) const network = await contract.gate!.signer.provider!.getNetwork() const networkName = WALLET_NETWORK_CHAIN_NAME(network.chainId) as 'amoy' // | 'sepolia' // | 'goerli' // | 'hardhat' const contracts = { // hardhat: CONTRACTS.IxilyActivV4_HardhatDemo, // goerli: CONTRACTS.IxilyActivV4_Goerli, // sepolia: CONTRACTS.IxilyActivV4_Sepolia, amoy: CONTRACTS.IxilyActivV4_Amoy, } const contractSelected = contracts[networkName] const settings: IWalletSettings = { ...contractSelected, callerAddress: callerAddress, balanceInEth, } return settings } const getRecordedContractCreds = (): | undefined | { userWalletPrivateKey?: string webProvider?: any } => { return state.recordCreds } export const ContractModule = { build, getUpdatedStream, listBlockchainNetworks, listContractsForNetwork, getBlockchainNetwork, getBlockchainContract, get, getGate, thisOrGet, thisOrGetCreator, thisOrGetGate, getWalletSettings, changeToContract, getRecordedContractCreds, }