UNPKG

scryptlib

Version:

Javascript SDK for integration of Bitcoin SV Smart Contracts written in sCrypt language.

787 lines 31.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildTypeResolver = exports.buildTypeResolverFromArtifact = exports.buildContractClass = exports.AbstractContract = exports.SUPPORTED_MINIMUM_VERSION = exports.CURRENT_CONTRACT_ARTIFACT_VERSION = void 0; const path_1 = require("path"); const _1 = require("."); const compilerWrapper_1 = require("./compilerWrapper"); const internal_1 = require("./internal"); const scryptTypes_1 = require("./scryptTypes"); const stateful_1 = require("./stateful"); const typeCheck_1 = require("./typeCheck"); exports.CURRENT_CONTRACT_ARTIFACT_VERSION = 9; exports.SUPPORTED_MINIMUM_VERSION = 8; class AbstractContract { // eslint-disable-next-line @typescript-eslint/no-empty-function /* eslint-disable @typescript-eslint/no-unused-vars */ constructor(...ctorParams) { this.calledPubFunctions = []; // This will be set to true, if the contract will expect inline ASM variable values to be set. this.hasInlineASMVars = false; this.hexTemplateInlineASM = new Map(); this.hexTemplateArgs = new Map(); this.statePropsArgs = []; // If true, the contract will read the state from property, if false, the contract will read the state from preimage // A newly constructed contract always has this set to true, and after invocation, always has it set to false this.isGenesis = true; } get lockingScript() { if (this.hasInlineASMVars && this.hexTemplateInlineASM.size === 0) { throw new Error('Values for inline ASM variables have not yet been set! Cannot get locking script.'); } if (!this.dataPart) { return this._wrapNOPScript(this.scriptedConstructor?.lockingScript); } // append dataPart script to codePart if there is dataPart return this.codePart.add(this.dataPart); } _wrapNOPScript(lockingScript) { if (this.nopScript) { return this.nopScript.clone().add(lockingScript); } return lockingScript; } set txContext(txContext) { this._txContext = txContext; } get txContext() { return this._txContext; } get sourceMapFile() { const artifact = Object.getPrototypeOf(this).constructor.artifact; return artifact.sourceMapFile; } get file() { const artifact = Object.getPrototypeOf(this).constructor.artifact; return artifact.file; } get contractName() { const artifact = Object.getPrototypeOf(this).constructor.artifact; return artifact.contract; } get stateProps() { const artifact = Object.getPrototypeOf(this).constructor.artifact; return artifact.stateProps || []; } get version() { const artifact = Object.getPrototypeOf(this).constructor.artifact; return artifact.version || 0; } addFunctionCall(f) { this.calledPubFunctions.push(f); } get resolver() { return Object.getPrototypeOf(this).constructor.resolver; } // replace assembly variables with assembly values replaceAsmVars(asmVarValues) { if (asmVarValues) { for (const key in asmVarValues) { const val = asmVarValues[key]; this.hexTemplateInlineASM.set(`<${key.startsWith('$') ? key.substring(1) : key}>`, internal_1.bsv.Script.fromASM(val).toHex()); } } const hexTemplate = Object.getPrototypeOf(this).constructor.hex; const lockingScript = (0, internal_1.buildContractCode)(this.hexTemplateArgs, this.hexTemplateInlineASM, hexTemplate); this.scriptedConstructor.lockingScript = lockingScript; } // replace assembly variables with assembly values get asmArgs() { const result = {}; for (const entry of this.hexTemplateInlineASM.entries()) { const name = entry[0].replace('<', '').replace('>', ''); const value = entry[1]; result[name] = internal_1.bsv.Script.fromHex(value).toASM(); } return result; } /** * @param states an object. Each key of the object is the name of a state property, and each value is the value of the state property. * @returns a locking script that includes the new states. If you only provide some but not all state properties, other state properties are not modified when calculating the locking script. */ getNewStateScript(states) { const stateArgs = this.statePropsArgs; if (stateArgs.length === 0) { throw new Error(`Contract ${this.contractName} does not have any stateful property`); } const resolveKeys = []; const newState = stateArgs.map(arg => { if (Object.prototype.hasOwnProperty.call(states, arg.name)) { resolveKeys.push(arg.name); let state = states[arg.name]; state = this.transformerArg(state, arg, true); const error = (0, typeCheck_1.checkSupportedParamType)(state, arg, this.resolver); if (error) { throw error; } return Object.assign({ ...arg }, { value: state }); } else { return arg; } }); Object.keys(states).forEach(key => { if (resolveKeys.indexOf(key) === -1) { throw new Error(`Contract ${this.contractName} does not have stateful property ${key}`); } }); return this.codePart.add(internal_1.bsv.Script.fromHex(stateful_1.default.buildState(newState, false, this.resolver))); } run_verify(unlockingScript, txContext) { const txCtx = Object.assign({}, this._txContext || {}, txContext || {}); let us; if (typeof unlockingScript === 'string') { us = unlockingScript.trim() ? internal_1.bsv.Script.fromASM(unlockingScript.trim()) : new internal_1.bsv.Script(''); } else { us = unlockingScript ? unlockingScript : new internal_1.bsv.Script(''); } const ls = internal_1.bsv.Script.fromHex(this.lockingScript.toHex()); const tx = typeof txCtx.tx === 'string' ? new internal_1.bsv.Transaction(txCtx.tx) : txCtx.tx; const inputIndex = txCtx.inputIndex; const inputSatoshis = txCtx.inputSatoshis; internal_1.bsv.Script.Interpreter.MAX_SCRIPT_ELEMENT_SIZE = Number.MAX_SAFE_INTEGER; internal_1.bsv.Script.Interpreter.MAXIMUM_ELEMENT_SIZE = Number.MAX_SAFE_INTEGER; const bsi = new internal_1.bsv.Script.Interpreter(); let failedAt = {}; bsi.stepListener = function (step) { if (step.fExec || (internal_1.bsv.Opcode.OP_IF <= step.opcode.toNumber() && step.opcode.toNumber() <= internal_1.bsv.Opcode.OP_ENDIF)) { if ((internal_1.bsv.Opcode.OP_IF <= step.opcode.toNumber() && step.opcode.toNumber() <= internal_1.bsv.Opcode.OP_ENDIF) || step.opcode.toNumber() === internal_1.bsv.Opcode.OP_RETURN) /**Opreturn */ { failedAt.opcode = step.opcode; } else { failedAt = step; } } }; const result = bsi.verify(us, ls, tx, inputIndex, internal_1.DEFAULT_FLAGS, new internal_1.bsv.crypto.BN(inputSatoshis)); if (result) { return { success: true, error: '' }; } if ((bsi.errstr || '').indexOf('SCRIPT_ERR_NULLFAIL') > -1) { if (!txCtx) { throw new Error('should provide txContext when verify'); } if (!tx) { throw new Error('should provide txContext.tx when verify'); } } failedAt.opcode = failedAt.opcode.toNumber(); return { success: result, error: this.fmtError({ error: bsi.errstr || '', failedAt }) }; } /** * format the error * @param err the result output by `tx.verifyInputScript(inputIndex)` * @returns string the formatted error message. */ fmtError(err) { const failedOpCode = err.failedAt.opcode; let error = `VerifyError: ${err.error}, fails at ${new internal_1.bsv.Opcode(failedOpCode)}\n`; if (this.sourceMapFile) { const sourceMapFilePath = (0, internal_1.uri2path)(this.sourceMapFile); const sourceMap = (0, internal_1.JSONParserSync)(sourceMapFilePath); const sourcePath = (0, path_1.join)(sourceMapFilePath, this.file); const srcDir = (0, path_1.dirname)(sourcePath); const sourceFileName = (0, path_1.basename)(sourcePath); const sources = sourceMap.sources.map((source) => (0, compilerWrapper_1.getFullFilePath)(source, srcDir, sourceFileName)); const pos = (0, internal_1.findSrcInfoV2)(err.failedAt.pc, sourceMap); if (pos && sources[pos[1]]) { error = `VerifyError: ${err.error} \n\t[Go to Source](${(0, internal_1.path2uri)(sources[pos[1]])}#${pos[2]}) fails at ${new internal_1.bsv.Opcode(failedOpCode)}\n`; } } else if (this.version <= 8) { const artifact = Object.getPrototypeOf(this).constructor.artifact; const sourceMap = (0, compilerWrapper_1.loadSourceMapfromArtifact)(artifact); if (sourceMap.length > 0) { // the complete script may have op_return and data, but compiled output does not have it. So we need to make sure the index is in boundary. const opcodeIndex = err.failedAt.pc; if (sourceMap[opcodeIndex]) { const opcode = sourceMap[opcodeIndex]; if (!opcode.pos || opcode.pos.file === 'std') { const srcInfo = (0, internal_1.findSrcInfoV1)(sourceMap, opcodeIndex); if (srcInfo) { opcode.pos = srcInfo.pos; } } // in vscode termianal need to use [:] to jump to file line, but here need to use [#] to jump to file line in output channel. if (opcode && opcode.pos) { error = `VerifyError: ${err.error} \n\t[Go to Source](${(0, internal_1.path2uri)(opcode.pos.file)}#${opcode.pos.line}) fails at ${new internal_1.bsv.Opcode(failedOpCode)}\n`; } } } } return error; } /** * Generate a debugger launch configuration for the contract's last called public method * @param txContext * @returns a uri of the debugger launch configuration */ genLaunchConfig(txContext) { const txCtx = Object.assign({}, this.txContext || {}, txContext || {}); const lastCalledPubFunction = this.lastCalledPubFunction(); if (lastCalledPubFunction) { const debugUrl = lastCalledPubFunction.genLaunchConfig(txCtx); return `[Launch Debugger](${debugUrl.replace(/file:/i, 'scryptlaunch:')})\n`; } throw new Error('No public function called'); } set dataPart(dataInScript) { throw new Error('Setter for dataPart is not available. Please use: setDataPart() instead'); } get dataPart() { if (AbstractContract.isStateful(this)) { const state = stateful_1.default.buildState(this.statePropsArgs, this.isGenesis, this.resolver); return internal_1.bsv.Script.fromHex(state); } if (this._dataPartInHex) { return internal_1.bsv.Script.fromHex(this._dataPartInHex); } } /** * @deprecated use setDataPartInASM setDataPartInHex * set the data part of the contract * @param state * @param isStateHex */ setDataPart(state, isStateHex = false) { if (isStateHex == false) { console.warn('deprecated, using setDataPartInASM'); this.setDataPartInASM(state); } else { console.warn('deprecated, using setDataPartInHex'); this.setDataPartInHex(state); } } /** * set the data part of the contract in ASM format * @param asm * @param */ setDataPartInASM(asm) { if (AbstractContract.isStateful(this)) { throw new Error('should not use `setDataPartInASM` for a stateful contract, using `setDataPartInHex`'); } const dataPartInASM = asm.trim(); this.setDataPartInHex(internal_1.bsv.Script.fromASM(dataPartInASM).toHex()); } /** * set the data part of the contract in hex format * @param hex */ setDataPartInHex(hex) { this._dataPartInHex = hex.trim(); if (AbstractContract.isStateful(this)) { const [isGenesis, args] = stateful_1.default.parseStateHex(this, this._dataPartInHex); this.statePropsArgs = args; this.isGenesis = isGenesis; } } prependNOPScript(nopScript) { if (nopScript instanceof internal_1.bsv.Script) { (0, internal_1.checkNOPScript)(nopScript); } this.nopScript = nopScript; } getPrependNOPScript() { return this.nopScript; } get codePart() { const contractScript = this.scriptedConstructor.toScript(); // note: do not trim the trailing space return this._wrapNOPScript(contractScript.clone()).add(internal_1.bsv.Script.fromHex('6a')); } get codeHash() { if (this.dataPart) { return (0, internal_1.hash160)(this.codePart.toHex()); } else { return (0, internal_1.hash160)(this.lockingScript.toHex()); } } static getAsmVars(lockingScriptHex) { const instance = this.fromHex(lockingScriptHex); return instance.asmArgs; } arguments(pubFuncName) { if (pubFuncName === 'constructor') { return this.scriptedConstructor.args; } for (let i = this.calledPubFunctions.length - 1; i >= 0; i--) { const called = this.calledPubFunctions[i]; if (called.methodName === pubFuncName) { return called.args; } } return []; } lastCalledPubFunction() { const index = this.calledPubFunctions.length - 1; if (index < 0) { return undefined; } return this.calledPubFunctions[index]; } ctorArgs() { return this.arguments('constructor'); } /** * Get the parameter of the constructor and inline asm vars, * all values is hex string, need convert it to number or bytes on using */ get asmVars() { return this.ContractClass.getAsmVars(this.scriptedConstructor.toHex()); } get ContractClass() { return Object.getPrototypeOf(this).constructor; } transformerArgs(args, params, state) { return params.map((p, index) => this.transformerArg(args[index], p, state)); } transformerArg(arg, param, state) { const typeInfo = this.resolver(param.type); if ((0, internal_1.isArrayType)(typeInfo.finalType)) { /* eslint-disable @typescript-eslint/no-unused-vars */ const [_, arraySizes] = (0, typeCheck_1.arrayTypeAndSize)(typeInfo.finalType); if (!Array.isArray(arg)) { return arg; } if (arg.length !== arraySizes[0]) { return arg; } const subType = (0, typeCheck_1.subArrayType)(param.type); const results = []; for (let i = 0; i < arraySizes[0]; i++) { const elem = arg[i]; results.push(this.transformerArg(elem, { name: `${param.name}${(0, internal_1.subscript)(i, arraySizes)}`, type: subType }, state)); } return results; } else if (typeInfo.symbolType === scryptTypes_1.SymbolType.Library) { const entity = typeInfo.info; if (entity.name === 'HashedMap') { if (arg instanceof Map) { if (state) { return { _data: this.ContractClass.toData(arg, param.type) }; } else { return [this.ContractClass.toData(arg, param.type)]; } } } else if (entity.name === 'HashedSet') { if (arg instanceof Set) { if (state) { return { _data: this.ContractClass.toData(arg, param.type) }; } else { return [this.ContractClass.toData(arg, param.type)]; } } } const params = state ? entity.properties : entity.params; if (!state && Array.isArray(arg)) { return params.map((p, index) => { return this.transformerArg(arg[index], p, state); }); } else if (state && typeof arg === 'object') { return params.reduce((acc, p) => { Object.assign(acc, { [p.name]: this.transformerArg(arg[p.name], p, state) }); return acc; }, {}); } } else if (typeInfo.symbolType === scryptTypes_1.SymbolType.Struct) { if (!Array.isArray(arg) && typeof arg === 'object') { const entity = typeInfo.info; if (entity.name === 'SortedItem') { if (arg['idx'] === (0, scryptTypes_1.Int)(-1) && (arg['image'] instanceof Map || arg['image'] instanceof Set)) { /* eslint-disable @typescript-eslint/no-unused-vars */ const [_, genericTypes] = (0, _1.parseGenericType)(typeInfo.finalType); return Object.assign({}, { idx: this.ContractClass.findKeyIndex(arg['image'], arg['item'], genericTypes[0]), item: arg['item'] }); } return arg; } const clone = Object.assign({}, arg); entity.params.forEach(property => { if (typeof arg[property.name] !== 'undefined') { clone[property.name] = this.transformerArg(arg[property.name], property, state); } }); return clone; } } else if (typeof arg === 'number') { return BigInt(arg); } return arg; } checkArgs(funname, params, ...args) { if (args.length !== params.length) { throw new Error(`wrong number of arguments for '${this.contractName}.${funname}', expected ${params.length} but got ${args.length}`); } const args_ = this.transformerArgs(args, params, false); params.forEach((param, index) => { const arg = args_[index]; const error = (0, typeCheck_1.checkSupportedParamType)(arg, param, this.resolver); if (error) throw error; }); return args_; } static fromASM(asm) { return this.fromHex(internal_1.bsv.Script.fromASM(asm).toHex()); } static fromHex(hex) { this.asmContract = true; const ctor = this; const obj = new ctor(); this.asmContract = false; obj.scriptedConstructor = this.abiCoder.encodeConstructorCallFromRawHex(obj, this.hex, hex); return obj; } static fromTransaction(hex, outputIndex = 0) { const tx = new internal_1.bsv.Transaction(hex); return this.fromHex(tx.outputs[outputIndex].script.toHex()); } static isStateful(contract) { return contract.stateProps.length > 0; } // struct / array: sha256 every single element of the flattened struct / array, and concat the result to a joint byte, and sha256 again // basic type: sha256 every single element static flattenSha256(data, type) { const error = (0, typeCheck_1.checkSupportedParamType)(data, { name: '', type: type }, this.resolver); if (error) throw error; const flattened = (0, typeCheck_1.flatternArg)({ name: '', type: type, value: data }, this.resolver, { state: true, ignoreValue: false }); if (flattened.length === 1) { const hex = stateful_1.default.serialize(flattened[0].value, flattened[0].type); return internal_1.bsv.crypto.Hash.sha256(Buffer.from(hex, 'hex')).toString('hex'); } else { const jointbytes = flattened.map(item => { const hex = stateful_1.default.serialize(item.value, item.type); return internal_1.bsv.crypto.Hash.sha256(Buffer.from(hex, 'hex')).toString('hex'); }).join(''); return internal_1.bsv.crypto.Hash.sha256(Buffer.from(jointbytes, 'hex')).toString('hex'); } } // sort the map by the result of flattenSha256 of the key static sortmap(map, keyType) { return new Map([...map.entries()].sort((a, b) => { return internal_1.bsv.crypto.BN.fromSM(Buffer.from(this.flattenSha256(a[0], keyType), 'hex'), { endian: 'little' }).cmp(internal_1.bsv.crypto.BN.fromSM(Buffer.from(this.flattenSha256(b[0], keyType), 'hex'), { endian: 'little' })); })); } // sort the set by the result of flattenSha256 of the key static sortset(set, keyType) { return new Set([...set.keys()].sort((a, b) => { return internal_1.bsv.crypto.BN.fromSM(Buffer.from(this.flattenSha256(a, keyType), 'hex'), { endian: 'little' }).cmp(internal_1.bsv.crypto.BN.fromSM(Buffer.from(this.flattenSha256(b, keyType), 'hex'), { endian: 'little' })); })); } static sortkeys(keys, keyType) { return keys.sort((a, b) => { return internal_1.bsv.crypto.BN.fromSM(Buffer.from(this.flattenSha256(a, keyType), 'hex'), { endian: 'little' }).cmp(internal_1.bsv.crypto.BN.fromSM(Buffer.from(this.flattenSha256(b, keyType), 'hex'), { endian: 'little' })); }); } // returns index of the HashedMap/HashedSet by the key static findKeyIndex(collection, key, keyType) { const keys = [...collection.keys()]; keys.push(key); const sortedKeys = this.sortkeys(keys, keyType); const index = sortedKeys.findIndex((entry) => { if (entry === key) { return true; } return false; }); return BigInt(index); } //serialize the HashedMap / HashedSet, but only flattenSha256 of the key and value static toData(collection, collectionType) { /* eslint-disable @typescript-eslint/no-unused-vars */ const [_, genericTypes] = (0, _1.parseGenericType)(collectionType); let storage = ''; if (collection instanceof Map) { const sortedMap = this.sortmap(collection, genericTypes[0]); for (const entry of sortedMap.entries()) { storage += this.flattenSha256(entry[0], genericTypes[0]) + this.flattenSha256(entry[1], genericTypes[1]); } } else { const sortedSet = this.sortset(collection, genericTypes[0]); for (const key of sortedSet.keys()) { storage += this.flattenSha256(key, genericTypes[0]); } } return (0, scryptTypes_1.Bytes)(storage); } } exports.AbstractContract = AbstractContract; const invalidMethodName = ['arguments', 'setDataPart', 'setDataPartInASM', 'setDataPartInHex', 'version', 'stateProps', 'sourceMapFile', 'file', 'contractName', 'ctorArgs', 'run_verify', 'replaceAsmVars', 'asmVars', 'asmArguments', 'dataPart', 'lockingScript', 'codeHash', 'codePart', 'resolver', 'getNewStateScript', 'txContext']; function buildContractClass(artifact) { if (artifact instanceof internal_1.CompileResult) { artifact = artifact.toArtifact(); } if (!artifact.contract) { throw new Error('Missing field `contract` in artifact'); } if (!artifact.version) { throw new Error('Missing field `version` in artifact'); } if (artifact.version < exports.SUPPORTED_MINIMUM_VERSION) { throw new Error(`Contract artifact version deprecated, The minimum version number currently supported is ${exports.SUPPORTED_MINIMUM_VERSION}`); } if (!artifact.abi) { throw new Error('Missing field `abi` in artifact'); } if (!artifact.hex) { throw new Error('Missing field `hex` in artifact'); } const ContractClass = class extends AbstractContract { constructor(...ctorParams) { super(); if (!ContractClass.asmContract) { this.scriptedConstructor = ContractClass.abiCoder.encodeConstructorCall(this, ContractClass.hex, ...ctorParams); } } }; ContractClass.artifact = artifact; ContractClass.resolver = buildTypeResolverFromArtifact(artifact); ContractClass.abi = artifact.abi; ContractClass.hex = artifact.hex; ContractClass.abiCoder = new internal_1.ABICoder(artifact.abi, ContractClass.resolver, artifact.contract); ContractClass.stateProps = artifact.stateProps || []; ContractClass.abi.forEach((entity) => { if (entity.type === _1.ABIEntityType.CONSTRUCTOR) { return; } if (!entity.name || invalidMethodName.indexOf(entity.name) > -1) { throw new Error(`Method name [${entity.name}] is used by scryptlib now, Pelease change you contract method name!`); } ContractClass.prototype[entity.name] = function (...args) { const call = ContractClass.abiCoder.encodePubFunctionCall(this, entity.name || '', args); this.addFunctionCall(call); return call; }; }); ContractClass.stateProps.forEach(p => { Object.defineProperty(ContractClass.prototype, p.name, { get() { const arg = this.statePropsArgs.find((arg) => { return arg.name === p.name; }); if (arg) { return arg.value; } else { throw new Error(`property ${p.name} does not exists`); } }, set(value) { const arg = this.statePropsArgs.find((arg) => { return arg.name === p.name; }); if (arg) { value = this.transformerArg(value, arg, true); const error = (0, typeCheck_1.checkSupportedParamType)(value, arg, this.resolver); if (error) throw error; arg.value = value; this.isGenesis = false; } else { throw new Error(`property ${p.name} does not exists`); } } }); }); return ContractClass; } exports.buildContractClass = buildContractClass; function buildTypeResolverFromArtifact(artifact) { const alias = artifact.alias || []; const library = artifact.library || []; const structs = artifact.structs || []; const contract = artifact.contract; return buildTypeResolver(contract, alias, structs, library); } exports.buildTypeResolverFromArtifact = buildTypeResolverFromArtifact; // build a resolver witch can only resolve type function buildTypeResolver(contract, alias, structs, library, contracts = [], statics = []) { const resolvedTypes = {}; structs.forEach(element => { resolvedTypes[element.name] = { info: element, generic: (0, typeCheck_1.hasGeneric)(element), finalType: element.name, symbolType: scryptTypes_1.SymbolType.Struct }; }); library.forEach(element => { resolvedTypes[element.name] = { info: element, generic: (0, typeCheck_1.hasGeneric)(element), finalType: element.name, symbolType: scryptTypes_1.SymbolType.Library }; }); contracts.forEach(element => { resolvedTypes[element.name] = { info: element, generic: (0, typeCheck_1.hasGeneric)(element), finalType: element.name, symbolType: scryptTypes_1.SymbolType.Contract }; }); // add std type resolvedTypes['HashedMap'] = { info: { name: 'HashedMap', params: [ { name: '_data', type: 'bytes' } ], properties: [ { name: '_data', type: 'bytes' } ], genericTypes: ['K', 'V'] }, generic: true, finalType: 'HashedMap', symbolType: scryptTypes_1.SymbolType.Library }; resolvedTypes['HashedSet'] = { info: { name: 'HashedSet', params: [ { name: '_data', type: 'bytes' } ], properties: [ { name: '_data', type: 'bytes' } ], genericTypes: ['E'] }, generic: true, finalType: 'HashedSet', symbolType: scryptTypes_1.SymbolType.Library }; resolvedTypes['SortedItem'] = { info: { name: 'SortedItem', params: [ { name: 'item', type: 'T' }, { name: 'idx', type: 'int' } ], genericTypes: ['T'] }, generic: true, finalType: 'SortedItem', symbolType: scryptTypes_1.SymbolType.Struct }; resolvedTypes['PubKeyHash'] = { finalType: 'Ripemd160', generic: false, symbolType: scryptTypes_1.SymbolType.ScryptType }; const resolver = (type) => { if (resolvedTypes[type]) { return resolvedTypes[type]; } if ((0, scryptTypes_1.isScryptType)(type)) { return { generic: false, finalType: type, symbolType: scryptTypes_1.SymbolType.ScryptType }; } return (0, internal_1.resolveType)(type, resolvedTypes, contract, statics, alias, library); }; return resolver; } exports.buildTypeResolver = buildTypeResolver; //# sourceMappingURL=contract.js.map