import { ArtifactFrom } from './artifact' ; import { AbiSymbol , Address , ArtifactSymbol , Name } from '../symbols' ; import { context } from '../context' ; import { Future , FutureBoolean , FutureBytes , FutureNumber , resolveBytesLike , resolveNumberLike } from '../values' ; import { AbiConstructorEntry } from '../abi' ; import { TransactionOverrides } from '../execute/execute' ; export type Contract < T > = { [ ArtifactSymbol ] : ArtifactFrom < T > ; [ Name ] : string ; [ Address ] : Future < string > ; } & { [ K in keyof T ] : T [ K ] extends ( ... args : infer A ) => infer R ? ( ... args : A ) => R : never ; } & { getStorageAt : ( address : string ) => Future < string > ; } ; export type ConstructorParams < T > = T extends { new ( ... args ) : any ; } ? A : any ; export interface NoParams { new ( ) : any ; } export interface WithParams { new ( first , ... args ) : any ; } export function contract < T extends NoParams > ( artifact ) ; export function contract < T extends NoParams > ( artifact , options ) ; export function contract < T extends NoParams > ( name , artifact ) ; export function contract < T extends NoParams > ( name , artifact , options ) ; export function contract < T extends WithParams > ( artifact , params ) ; export function contract < T extends WithParams > ( artifact , params , options ) ; export function contract < T extends WithParams > ( name , artifact , params ) ; export function contract < T extends WithParams > ( name , artifact , params , options ) ; export function contract ( ... args ) { context . ensureEnabled ( ) ; const { name , artifact , params , options } = parseContractArgs ( ... args ) ; const constructor = artifact [ AbiSymbol ] . find ( ( { type } ) => type === 'constructor' ) as AbiConstructorEntry ; const [ address , resolveAddress ] = Future . create < string > ( ) ; context . actions . push ( { type : 'DEPLOY' , name , constructor , artifact , params , options , resolve : resolveAddress , skipUpgrade : ! ! options . skipUpgrade , multisig : context . multisig ?. current ( ) , } ) ; return makeContractInstance ( name , artifact , address ) ; } function unCapitalize ( value ) { return value !== '' ? `${ value [ 0 ] . toLowerCase ( ) }${ value . substring ( 1 ) }` : '' ; } function parseContractArgs ( ... args ) { const withName = typeof args [ 0 ] === 'string' ; const artifactIndex = withName ? 1 : 0 ; const artifact = args [ artifactIndex ] ; const name = withName ? args [ 0 ] : unCapitalize ( artifact [ Name ] ) ; const withParams = Array . isArray ( args [ artifactIndex + 1 ] ) ; const params = withParams ? args [ artifactIndex + 1 ] : [ ] ; const options = ( withParams ? args [ artifactIndex + 2 ] : args [ artifactIndex + 1 ] ) ?? { } ; return { name , artifact , options , params } ; } export function makeContractInstance < T > ( name , artifact , address ) { const contract = { [ ArtifactSymbol ] : artifact , [ Address ] : address , [ Name ] : name , } ; for ( const entry of artifact [ AbiSymbol ] ) { if ( entry . type === 'function' ) { contract [ entry . name ] = ( ... args ) => { context . ensureEnabled ( ) ; const [ result , resolveResult ] = Future . create ( ) ; const isView = [ 'pure' , 'view' ] . includes ( entry . stateMutability ) ; let options = { } ; let params = args ; if ( ! isView && args . length > entry . inputs . length ) { options = args [ args . length - 1 ] ; params = params . slice ( 0 , args . length - 1 ) ; } context . actions . push ( { type : isView ? 'READ' : 'TRANSACTION' , name , address : address , method : entry , params , options , resolve : resolveResult , multisig : context . multisig ?. current ( ) , } ) ; const type = entry . outputs ?. [ 0 ] ?. type ; const length = entry . outputs ?. length ; return type && length === 1 && isView ? castFuture ( type , result ) : result ; } ; } } contract . getStorageAt = ( storageAddress ) => { context . ensureEnabled ( ) ; const [ result , resolveResult ] = Future . create ( ) ; context . actions . push ( { type : 'GET_STORAGE_AT' , address , storageAddress , resolve : resolveResult , } ) ; return result ; } ; return contract ; } function castFuture ( type , future ) { if ( type . startsWith ( 'uint' ) || type . startsWith ( 'int' ) ) { return new FutureNumber ( future . map ( resolveNumberLike ) . resolve ) ; } else if ( type === 'bool' ) { return new FutureBoolean ( future . resolve ) ; } else if ( type . startsWith ( 'byte' ) ) { return new FutureBytes ( future . map ( resolveBytesLike ) . resolve ) ; } else { return future ; } }