github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/js/src/contracts/compile.ts (about) 1 import fs from 'fs'; 2 import { ResolvedImport } from 'solc'; 3 import solc from 'solc_v5'; 4 import { format } from 'util'; 5 import { ABI } from './abi'; 6 import { Contract } from './contract'; 7 8 export type CompiledContract = { 9 abi: ABI; 10 // Required to deploy a contract 11 bytecode?: string; 12 // Required to submit an ABI when deploying a contract 13 deployedBytecode?: string; 14 }; 15 16 export namespace Solidity { 17 export type Bytecode = { 18 linkReferences: any; 19 object: string; 20 opcodes: string; 21 sourceMap: string; 22 }; 23 24 export type Contract = { 25 assembly: any; 26 evm: { 27 bytecode: Bytecode; 28 deployedBytecode: Bytecode; 29 }; 30 functionHashes: any; 31 gasEstimates: any; 32 abi: ABI.FunctionOrEvent[]; 33 opcodes: string; 34 runtimeBytecode: string; 35 srcmap: string; 36 srcmapRuntime: string; 37 }; 38 39 export type Source = { 40 AST: any; 41 }; 42 43 export type InputDescription = { 44 language: string; 45 sources: Record<string, { content: string }>; 46 settings: { 47 outputSelection: Record<string, Record<string, Array<string>>>; 48 }; 49 }; 50 51 export type Error = { 52 sourceLocation?: { 53 file: string; 54 start: number; 55 end: number; 56 }; 57 type: string; 58 component: string; 59 severity: 'error' | 'warning'; 60 message: string; 61 formattedMessage?: string; 62 }; 63 64 export type OutputDescription = { 65 contracts: Record<string, Record<string, Contract>>; 66 errors?: Array<Error>; 67 sourceList: Array<string>; 68 sources: Record<string, Source>; 69 }; 70 } 71 72 // Compile solidity source code 73 export function compile<T = any>( 74 source: string, 75 name: string, 76 fatalErrorSeverity: 'error' | 'warning' = 'error', 77 ): Contract<T> { 78 const desc: solc.InputDescription = { language: 'Solidity', sources: {} }; 79 if (!desc.sources) { 80 desc.sources = {}; 81 } 82 desc.sources[name] = { content: source }; 83 desc.settings = { outputSelection: { '*': { '*': ['*'] } } }; 84 85 const json = solc.compile(JSON.stringify(desc)); 86 const compiled: solc.OutputDescription = JSON.parse(json); 87 const fatalErrors = compiled.errors?.filter((err) => err.severity === fatalErrorSeverity) ?? []; 88 if (fatalErrors.length) { 89 throw new Error(fatalErrors.map((err) => err.formattedMessage).toString()); 90 } 91 const contract = compiled.contracts[name][name]; 92 return new Contract( 93 getCompiledCode(contract), 94 Object.entries(compiled.contracts[name]) 95 .filter(([n]) => n !== name) 96 .map(([n, c]) => getCompiledCode(c)), 97 ); 98 } 99 100 function getCompiledCode(contract: solc.Contract): Required<CompiledContract> { 101 return { 102 abi: contract.abi, 103 bytecode: contract.evm.bytecode.object, 104 deployedBytecode: contract.evm.deployedBytecode.object, 105 }; 106 } 107 108 function NewInputDescription(): Solidity.InputDescription { 109 return { 110 language: 'Solidity', 111 sources: {}, 112 settings: { outputSelection: {} }, 113 }; 114 } 115 116 export function encodeInput(obj: Solidity.InputDescription): string { 117 return JSON.stringify(obj); 118 } 119 120 export function decodeOutput(str: string): Solidity.OutputDescription { 121 return JSON.parse(str); 122 } 123 124 export function inputDescriptionFromFiles(names: string[]): Solidity.InputDescription { 125 const desc = NewInputDescription(); 126 names.map((name) => { 127 desc.sources[name] = { content: fs.readFileSync(name).toString() }; 128 desc.settings.outputSelection[name] = {}; 129 desc.settings.outputSelection[name]['*'] = ['*']; 130 }); 131 return desc; 132 } 133 134 export function importLocalResolver(basePath: string): (path: string) => ResolvedImport { 135 return (path) => { 136 try { 137 return { 138 contents: fs.readFileSync(path).toString(), 139 }; 140 } catch (err) { 141 throw new Error(`could not import path '${path}': ${format(err)}`); 142 } 143 }; 144 } 145 146 export function tokenizeLinks(links: Record<string, Record<string, unknown>>): string[] { 147 const libraries: Array<string> = []; 148 for (const file in links) { 149 for (const library in links[file]) { 150 libraries.push(file + ':' + library); 151 } 152 } 153 return libraries; 154 } 155 156 export function linker(bytecode: string, links: { name: string; address: string }[]): string { 157 for (const { name, address } of links) { 158 const paddedAddress = address + Array(40 - address.length + 1).join('0'); 159 const truncated = name.slice(0, 36); 160 const label = '__' + truncated + Array(37 - truncated.length).join('_') + '__'; 161 while (bytecode.indexOf(label) >= 0) { 162 bytecode = bytecode.replace(label, paddedAddress); 163 } 164 } 165 return bytecode; 166 }