quasvel
Version:
Access and interact with Aragon Organizations and their apps.
181 lines (151 loc) • 4.51 kB
text/typescript
import {
getDefaultProvider as getDefaultEthersProvider,
providers as ethersProviders,
utils as ethersUtils,
} from 'ethers'
import {
ConnectorJson,
ConnectorJsonConfig,
IOrganizationConnector,
Organization,
toNetwork,
} from '@aragon/connect-core'
import ConnectorEthereum, {
ConnectorEthereumConfig,
} from '@aragon/connect-ethereum'
import ConnectorTheGraph, {
ConnectorTheGraphConfig,
} from '@aragon/connect-thegraph'
import { Address, Network, Networkish } from '@aragon/connect-types'
import { XDAI_WSS_ENDPOINT, DEFAULT_IPFS_URL } from './constants'
export type IpfsUrlResolver = (cid: string, path?: string) => string
export type ConnectOptions = {
actAs?: string
ethereum?: object
ipfs?: IpfsUrlResolver | string
network?: Networkish
verbose?: boolean
}
export type ConnectorDeclaration =
| IOrganizationConnector
| ['ethereum', ConnectorEthereumConfig | undefined]
| ['json', ConnectorJsonConfig | undefined]
| ['thegraph', ConnectorTheGraphConfig | undefined]
| [string, any]
| string
type IpfsResolver = (cid: string, path?: string) => string
function ipfsResolverFromUrlTemplate(urlTemplate: string): IpfsResolver {
return function ipfsResolver(cid: string, path?): string {
const url = urlTemplate.replace(/\{cid\}/, cid)
if (!path) {
return url.replace(/\{path\}/, '')
}
if (!path.startsWith('/')) {
path = `/${path}`
}
return url.replace(/\{path\}/, path)
}
}
function getIpfsResolver(ipfs: ConnectOptions['ipfs']) {
if (typeof ipfs === 'function') {
return ipfs
}
return ipfsResolverFromUrlTemplate(ipfs || DEFAULT_IPFS_URL)
}
function normalizeConnectorConfig(
connector: ConnectorDeclaration
): [string, any] | null {
if (Array.isArray(connector)) {
return [connector[0], connector[1] || {}]
}
if (typeof connector === 'string') {
return [connector, {}]
}
return null
}
function getConnector(
connector: ConnectorDeclaration,
network: Network
): IOrganizationConnector {
const normalizedConfig = normalizeConnectorConfig(connector)
if (normalizedConfig === null) {
return connector as IOrganizationConnector
}
const [name, config] = normalizedConfig
if (!config.network) {
config.network = network
}
if (name === 'json') {
return new ConnectorJson(config as ConnectorJsonConfig)
}
if (name === 'thegraph') {
return new ConnectorTheGraph(config as ConnectorTheGraphConfig)
}
if (name === 'ethereum') {
return new ConnectorEthereum(config as ConnectorEthereumConfig)
}
throw new Error(`Unsupported connector name: ${name}`)
}
function getEthersProvider(
ethereumProvider: object | undefined,
network: Network
): ethersProviders.Provider {
// Ethers compatibility: ethereum => homestead
if (network.name === 'ethereum' && network.chainId === 1) {
network = { ...network, name: 'homestead' }
}
if (ethereumProvider) {
try {
return new ethersProviders.Web3Provider(ethereumProvider, network)
} catch (err) {
console.error('Invalid provider:', ethereumProvider)
throw err
}
}
if (network.chainId === 100) {
return new ethersProviders.WebSocketProvider(XDAI_WSS_ENDPOINT, network)
}
return getDefaultEthersProvider(network)
}
async function resolveAddress(
ethersProvider: ethersProviders.Provider,
location: string
): Promise<Address> {
const address = ethersUtils.isAddress(location)
? location
: await ethersProvider.resolveName(location)
if (!ethersUtils.isAddress(address)) {
throw new Error('Please provide a valid address or ENS domain.')
}
return address
}
async function connect(
location: string,
connector: ConnectorDeclaration,
{
actAs,
ethereum: ethereumProvider,
ipfs,
network,
verbose,
}: ConnectOptions = {}
): Promise<Organization> {
const _network = toNetwork(network ?? 'ethereum')
const ethersProvider = getEthersProvider(ethereumProvider, _network)
const orgConnector = getConnector(connector, _network)
const orgAddress = await resolveAddress(ethersProvider, location)
const connectionContext = {
actAs: actAs || null,
ethereumProvider: ethereumProvider || null,
ethersProvider,
ipfs: getIpfsResolver(ipfs),
network: _network,
orgAddress,
orgConnector,
orgLocation: location,
verbose: verbose ?? false,
}
await orgConnector.connect?.(connectionContext)
return new Organization(connectionContext)
}
export default connect