github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/js/src/solts/lib/contract.ts (about) 1 import ts, { factory, ObjectLiteralElementLike } from 'typescript'; 2 import { defaultCallName } from './caller'; 3 import { decodeName } from './decoder'; 4 import { encodeName } from './encoder'; 5 import { 6 BoundsType, 7 CallbackReturnType, 8 createListener, 9 createListenerForFunction, 10 dataFromEvent, 11 eventSigHash, 12 topicsFromEvent, 13 } from './events'; 14 import { errName, EventErrParameter, eventName, EventParameter, Provider } from './provider'; 15 import { ContractMethodsList, getRealType, inputOuputsToType, Signature } from './solidity'; 16 import { 17 asConst, 18 constObject, 19 createCall, 20 createCallbackType, 21 createParameter, 22 createPromiseOf, 23 declareConstant, 24 EqualsGreaterThanToken, 25 ExportToken, 26 MaybeUint8ArrayType, 27 Method, 28 prop, 29 ReturnType, 30 StringType, 31 Undefined, 32 UnknownType, 33 } from './syntax'; 34 35 export const contractFunctionName = factory.createIdentifier('contract'); 36 export const contractTypeName = factory.createIdentifier('Contract'); 37 export const functionsGroupName = factory.createIdentifier('functions'); 38 export const listenersGroupName = factory.createIdentifier('listeners'); 39 const dataName = factory.createIdentifier('data'); 40 const clientName = factory.createIdentifier('client'); 41 const addressName = factory.createIdentifier('address'); 42 const listenerForName = factory.createIdentifier('listenerFor'); 43 const listenerName = factory.createIdentifier('listener'); 44 45 export function declareContractType(): ts.TypeAliasDeclaration { 46 return factory.createTypeAliasDeclaration( 47 undefined, 48 [ExportToken], 49 contractTypeName, 50 undefined, 51 factory.createTypeReferenceNode(ReturnType, [factory.createTypeQueryNode(contractFunctionName)]), 52 ); 53 } 54 55 export function generateContractObject( 56 contractName: string, 57 abi: ContractMethodsList, 58 provider: Provider, 59 ): ts.VariableStatement { 60 const functions = abi.filter((a) => a.type === 'function'); 61 const events = abi.filter((a) => a.type === 'event'); 62 63 const functionObjectProperties = functions.length 64 ? [ 65 createGroup( 66 functionsGroupName, 67 functions.flatMap((a) => 68 a.signatures.map((signature, index) => solidityFunction(a.name, a.signatures, index)), 69 ), 70 ), 71 ] 72 : []; 73 74 const eventObjectProperties = events.length 75 ? [ 76 createGroup( 77 listenersGroupName, 78 events.map((a) => solidityEvent(a.name, a.signatures[0], provider)), 79 ), 80 factory.createPropertyAssignment(listenerForName, createListenerForFunction(clientName, addressName)), 81 factory.createPropertyAssignment(listenerName, createListener(clientName, addressName)), 82 ] 83 : []; 84 85 return declareConstant( 86 contractFunctionName, 87 factory.createArrowFunction( 88 undefined, 89 undefined, 90 [createParameter(clientName, provider.type()), createParameter(addressName, StringType)], 91 undefined, 92 EqualsGreaterThanToken, 93 asConst( 94 factory.createObjectLiteralExpression([ 95 factory.createPropertyAssignment('name', factory.createStringLiteral(contractName)), 96 factory.createShorthandPropertyAssignment(addressName), 97 ...functionObjectProperties, 98 ...eventObjectProperties, 99 ]), 100 ), 101 ), 102 true, 103 ); 104 } 105 106 function solidityFunction(name: string, signatures: Signature[], index: number): ts.MethodDeclaration { 107 const signature = signatures[index]; 108 const args = signature.inputs.map((input) => factory.createIdentifier(input.name)); 109 const encodeFunctionOrOverloadsArray = prop(createCall(encodeName, [clientName]), name); 110 const callName = factory.createIdentifier('call'); 111 112 // Special case for overloads 113 const hasOverloads = signatures.length > 1; 114 115 const encoderFunction = hasOverloads 116 ? factory.createElementAccessExpression(encodeFunctionOrOverloadsArray, index) 117 : encodeFunctionOrOverloadsArray; 118 119 const decoderFunctionOrOverloadsArray = prop(createCall(decodeName, [clientName, dataName]), name); 120 121 const decoderFunction = hasOverloads 122 ? factory.createElementAccessExpression(decoderFunctionOrOverloadsArray, index) 123 : decoderFunctionOrOverloadsArray; 124 125 const encode = declareConstant(dataName, createCall(encoderFunction, args)); 126 127 const returnType = inputOuputsToType(signature.outputs); 128 129 const call = factory.createCallExpression( 130 callName, 131 [returnType], 132 [ 133 clientName, 134 addressName, 135 dataName, 136 signature.constant ? factory.createTrue() : factory.createFalse(), 137 factory.createArrowFunction( 138 undefined, 139 undefined, 140 [createParameter(dataName, MaybeUint8ArrayType)], 141 undefined, 142 undefined, 143 factory.createBlock([factory.createReturnStatement(createCall(decoderFunction, []))], true), 144 ), 145 ], 146 ); 147 148 const callParameter = createParameter(callName, undefined, defaultCallName); 149 150 const params = signature.inputs.map((input) => createParameter(input.name, getRealType(input.type))); 151 // Suffix overloads 152 return new Method(index > 0 ? `${name}_${index}` : name) 153 .parameters(params) 154 .parameters(callParameter) 155 .returns(createPromiseOf(returnType)) 156 .declaration([encode, factory.createReturnStatement(call)], true); 157 } 158 159 function solidityEvent(name: string, signature: Signature, provider: Provider): ts.MethodDeclaration { 160 const callback = factory.createIdentifier('callback'); 161 const start = factory.createIdentifier('start'); 162 const end = factory.createIdentifier('end'); 163 // Receivers of LogEventParameter 164 const data = dataFromEvent(eventName); 165 const topics = topicsFromEvent(eventName); 166 const decoderFunction = prop(createCall(decodeName, [clientName, data, topics]), name); 167 return ( 168 new Method(name) 169 .parameter( 170 callback, 171 createCallbackType( 172 [EventErrParameter, createParameter(eventName, inputOuputsToType(signature.inputs), undefined, true)], 173 CallbackReturnType, 174 ), 175 ) 176 .parameter(start, BoundsType, true) 177 .parameter(end, BoundsType, true) 178 // type may be EventStream, allow type assertion without polluting inteface 179 .returns(UnknownType) 180 .declaration([ 181 factory.createReturnStatement( 182 provider.methods.listen.call( 183 clientName, 184 factory.createArrayLiteralExpression([factory.createStringLiteral(eventSigHash(name, signature.inputs))]), 185 addressName, 186 187 factory.createArrowFunction( 188 undefined, 189 undefined, 190 [EventErrParameter, EventParameter], 191 undefined, 192 undefined, 193 factory.createBlock([ 194 factory.createIfStatement(errName, factory.createReturnStatement(createCall(callback, [errName]))), 195 factory.createReturnStatement(createCall(callback, [Undefined, createCall(decoderFunction)])), 196 ]), 197 ), 198 start, 199 end, 200 ), 201 ), 202 ]) 203 ); 204 } 205 206 function createGroup(name: ts.Identifier, elements: ObjectLiteralElementLike[]): ts.PropertyAssignment { 207 return factory.createPropertyAssignment(name, constObject(elements)); 208 }