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  }