import { ConstructorParams , contract , Contract , makeContractInstance , NoParams , WithParams } from './contract' ; import { Address , ArtifactSymbol , Name } from '../symbols' ; import { Future } from '../values' ; import { constants , utils } from 'ethers' ; import { ArtifactFrom } from './artifact' ; import { runIf } from './conditionals' ; type Params < T > = T extends ( ... args : infer A ) => any ? A : never ; /**
 * Optional arguments for proxy instance creation
 */ type ProxyOptionals < T , U extends keyof T > = { /**
     * Custom name for the proxied contract
     */ name ? : string ; /**
     * Routine to be executed at first deployment
     */ onInitialize ? : U ; /**
     * Params for the initialization routine
     */ params ? : Params < T [ U ] > ; /**
     * If set to true, it prevents proxied contract from being redeployed when e.g. ctor argument containing initial
     * implementation address changes.
     */ noRedeploy ? : boolean ; /**
     * If set to true, it prevents implementation upgrade in proxy contracts. Useful in multisig scenario where proxy
     * does not exist and there is no possibility to check existing implementation behind proxy in order to reason about
     * implementation upgrading.
     */ noImplUpgrade ? : boolean ; } ; /**
 * Proxy that wraps contract implementations. Exposes factory methods that create wrapped instances of contracts.
 * Such proxy instances take care of specific contract proxies deployment, initialization and upgrades if underlying
 * implementation changes.
 */ export interface Proxy { param contract param onInitialize < T > ( contract , onInitialize ? ) : Contract < T > ; param contract param onInitialize param params < T , U extends keyof T > ( contract , onInitialize ? , params ? ) : Contract < T > ; param name param contract param onInitialize < T > ( name , contract , onInitialize ? ) : Contract < T > ; param name param contract param onInitialize param params < T , U extends keyof T > ( name , contract , onInitialize ? , params ? ) : Contract < T > ; param contract param optionals < T , U extends keyof T > ( contract , optionals ) : Contract < T > ; } type MethodCall < T > = keyof T | ( ( contract : Contract < T > ) => unknown ) ; function getImplementation ( proxy ) { const IMPLEMENTATION_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' ; if ( proxy . implementation ) { return proxy . implementation ( ) ; } const stored64Bytes = proxy . getStorageAt ( IMPLEMENTATION_SLOT ) ; const extractAddress = ( slot ) => utils . getAddress ( `0x${ slot . slice ( - 40 ) }` ) ; return stored64Bytes . map ( extractAddress ) ; } export function createProxy < T extends NoParams > ( artifact , onUpgrade ? ) ; export function createProxy < T extends WithParams > ( artifact , params , onUpgrade ? ) ; export function createProxy ( ... args ) { const artifact = args [ 0 ] ; const params = Array . isArray ( args [ 1 ] ) ? args [ 1 ] : [ ] ; const onUpgradeIndex = params . length > 0 ? 2 : 1 ; const onUpgrade = args [ onUpgradeIndex ] ?? 'upgradeTo' ; return ( ... args ) => { const [ name , implementation , onInitialize , noRedeploy , noImplUpgrade ] = parseProxyArgs ( ... args ) ; const proxy = contract < { new ( ... args ) : void ; implementation ( ) : Future < string > ; } > ( name ?? `${ implementation [ Name ] }_proxy` , artifact as any , params , { skipUpgrade : noRedeploy } ) ; let currentImplementation ; if ( ! noImplUpgrade ) { currentImplementation = getImplementation ( proxy ) ; const normalizedOnUpgrade = normalizeCall ( proxy , onUpgrade , [ implementation ] ) ; runIf ( currentImplementation . equals ( implementation [ Address ] ) . not ( ) , ( ) => normalizedOnUpgrade ( proxy ) ) ; } const contractBehindProxy = makeContractInstance ( implementation [ Name ] , implementation [ ArtifactSymbol ] , proxy [ Address ] ) ; if ( ! noImplUpgrade && currentImplementation ) runIf ( currentImplementation . equals ( constants . AddressZero ) , ( ) => onInitialize && onInitialize ( contractBehindProxy ) ) ; return contractBehindProxy ; } ; } function parseProxyArgs ( ... args ) { const hasObjectParam = args . length == 2 && typeof args [ 1 ] !== 'function' && typeof args [ 1 ] !== 'string' ; const objectParam = ( hasObjectParam ? args [ 1 ] : { } ) as ProxyOptionals < any , any > ; const withName = typeof args [ 0 ] === 'string' ; const name = withName ? args [ 0 ] : objectParam . name ; const contract = args [ withName ? 1 : 0 ] ; const onInitialize = hasObjectParam ? objectParam . onInitialize : args [ withName ? 2 : 1 ] ; const onInitializeParams = ( hasObjectParam ? objectParam . params : args [ withName ? 3 : 2 ] ) ?? [ ] ; const onInitializeNormalized = onInitialize ? normalizeCall ( contract , onInitialize , onInitializeParams ) : undefined ; const noRedeploy = objectParam . noRedeploy ?? false ; const noImplUpgrade = objectParam . noImplUpgrade ?? false ; return [ name , contract , onInitializeNormalized , noRedeploy , noImplUpgrade ] ; } function normalizeCall < T > ( contract , call , params ) { if ( typeof call === 'function' ) { return call ; } const methodName = call ; return ( contract ) => contract [ methodName ] ( ... params ) ; }