├── .github ├── FUNDING.yml └── workflows │ └── main.yml ├── .gitignore ├── .nosana-ci.yml ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── bindings ├── compile.ts ├── core.ts ├── helpers.ts └── index.ts ├── common.ts ├── common_test.ts ├── deno.json ├── deno.lock ├── deps.ts ├── download.ts ├── download_test.ts ├── egg.json ├── examples ├── erc20 │ ├── ERC20.sol │ ├── MyToken.sol │ └── mod.ts └── with-library │ ├── Example.sol │ ├── LibString.sol │ └── mod.ts ├── helpers_test.ts ├── logo.png ├── mod.ts ├── mod_test.ts └── types.ts /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: v1rtl 2 | liberapay: v1rtl 3 | custom: ["https://v1rtl.site/support"] 4 | issuehunt: talentlessguy 5 | github: [talentlessguy] 6 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | # Controls when the action will run. Triggers the workflow on push or pull request 4 | # events but only for the master branch 5 | on: 6 | push: 7 | branches: [master] 8 | pull_request: 9 | branches: [master] 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Cache dependencies 18 | uses: actions/cache@v4 19 | with: 20 | key: ${{ runner.os }}-deno-${{ hashFiles('**/*') }} 21 | restore-keys: ${{ runner.os }}-deno- 22 | path: /home/runner/.cache/deno/deps/https/deno.land 23 | 24 | - uses: denoland/setup-deno@v2 25 | with: 26 | deno-version: v2.x 27 | 28 | - name: fetch any uncached dependencies 29 | run: | 30 | deno cache --reload=file: ./mod.ts 31 | 32 | - name: Run tests 33 | run: deno task test 34 | - name: Create coverage report 35 | run: deno task cov 36 | - name: Coveralls 37 | uses: coverallsapp/github-action@master 38 | with: 39 | github-token: ${{ secrets.GITHUB_TOKEN }} 40 | path-to-lcov: ./coverage.lcov 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | *.cjs 3 | coverage* -------------------------------------------------------------------------------- /.nosana-ci.yml: -------------------------------------------------------------------------------- 1 | # .nosana-ci.yml 2 | nosana: 3 | description: Nosana Deno Template 4 | 5 | global: 6 | image: denoland/deno:latest 7 | 8 | # Git, trigger on these branches 9 | trigger: 10 | branch: 11 | - master 12 | 13 | jobs: 14 | # Init Deno 15 | - name: install 16 | commands: 17 | - deno cache --reload=file: ./mod.ts 18 | - deno task test 19 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true, 3 | "deno.lint": true, 4 | "deno.config": "./deno.json", 5 | "deno.suggest.imports.hosts": { 6 | "https://deno.land/x/": true, 7 | "https://x.nest.land/": true, 8 | "https://esm.sh": false 9 | }, 10 | "[typescript]": { 11 | "editor.defaultFormatter": "denoland.vscode-deno" 12 | }, 13 | "editor.formatOnSave": true 14 | } 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 deno-web3 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | logo 4 | 5 | # solc 6 | 7 | [![GitHub Workflow Status][gh-actions-img]][github-actions] 8 | [![Codecov][cov-badge-url]][cov-url] [![][code-quality-img]][code-quality] 9 | 10 |
11 | 12 | Solidity bindings for Deno, based on [solc-js](https://github.com/ethereum/solc-js). 13 | 14 | Solidity 0.7+ is supported. 15 | 16 | For a CLI and a higher level API you can use [sol_build](https://github.com/deno-web3/sol_build). 17 | 18 | ## Docs 19 | 20 | See [solc-js README](https://github.com/ethereum/solc-js#readme) and [Deno doc](https://deno.land/x/solc/mod.ts). 21 | 22 | ## Example 23 | 24 | ```ts 25 | import { wrapper } from '@deno-web3/solc' 26 | import { Input } from '@deno-web3/solc/types' 27 | import { download } from '@deno-web3/solc/download' 28 | import { createRequire } from 'node:module' 29 | 30 | // Download latest Solidity compiler 31 | await download() 32 | 33 | const solc = wrapper(createRequire(import.meta.url)('./soljson.cjs')) 34 | 35 | const MyToken = await Deno.readTextFile('./MyToken.sol') 36 | const ERC20 = await Deno.readTextFile('./ERC20.sol') 37 | 38 | const input: Input = { 39 | language: 'Solidity', 40 | sources: { 41 | 'MyToken.sol': { 42 | content: MyToken, 43 | }, 44 | 'ERC20.sol': { 45 | content: ERC20, 46 | }, 47 | }, 48 | settings: { 49 | outputSelection: { 50 | '*': { 51 | '*': ['*'], 52 | }, 53 | }, 54 | }, 55 | } 56 | console.log(JSON.parse(solc.compile(JSON.stringify(input)))) 57 | ``` 58 | 59 | And then run with 60 | 61 | ```sh 62 | deno run --allow-net --allow-read --allow-write mod.ts 63 | ``` 64 | 65 | [code-quality-img]: https://img.shields.io/codefactor/grade/github/deno-web3/solc?style=for-the-badge&color=626890& 66 | [code-quality]: https://www.codefactor.io/repository/github/deno-web3/solc 67 | [cov-badge-url]: https://img.shields.io/coveralls/github/deno-web3/solc?style=for-the-badge&color=626890& 68 | [cov-url]: https://coveralls.io/github/deno-web3/solc 69 | [github-actions]: https://github.com/tinyhttp/deno-web3/solc 70 | [gh-actions-img]: https://img.shields.io/github/actions/workflow/status/deno-web3/solc/main.yml?branch=master&style=for-the-badge&color=626890&label=&logo=github 71 | -------------------------------------------------------------------------------- /bindings/compile.ts: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert' 2 | 3 | import { bindSolcMethod } from './helpers.ts' 4 | import type { Callbacks, CompileBindings, CompileJsonStandard, CoreBindings, ReadCallback, SolJson } from 'solc/types' 5 | 6 | export function setupCompile(solJson: SolJson, core: CoreBindings): CompileBindings { 7 | return { 8 | compileStandard: bindCompileStandard(solJson, core), 9 | } 10 | } 11 | 12 | /********************** 13 | * COMPILE 14 | **********************/ 15 | 16 | /** 17 | * Returns a binding to the solidity solidity_compile method. 18 | * input (jsontext), callback (optional >= v6 only - ptr) -> output (jsontext) 19 | * 20 | * @param solJson The Emscripten compiled Solidity object. 21 | * @param coreBindings The core bound Solidity methods. 22 | */ 23 | function bindCompileStandard(solJson: SolJson, coreBindings: CoreBindings): CompileJsonStandard { 24 | // input (jsontext), callback (ptr), callback_context (ptr) -> output (jsontext) 25 | const boundFunctionSolidity: (jsontext: string, ptr?: number) => string = bindSolcMethod( 26 | solJson, 27 | 'solidity_compile', 28 | 'string', 29 | ['string', 'number', 'number'], 30 | null, 31 | ) 32 | 33 | const boundFunctionStandard = function (input: string, callbacks: Callbacks) { 34 | return runWithCallbacks(solJson, coreBindings, callbacks, boundFunctionSolidity, [input]) 35 | } 36 | 37 | return boundFunctionStandard as unknown as CompileJsonStandard 38 | } 39 | 40 | /********************** 41 | * CALL BACKS 42 | **********************/ 43 | 44 | function wrapCallbackWithKind( 45 | coreBindings: CoreBindings, 46 | callback: (...args: Arg[]) => Partial<{ contents: unknown; error: unknown }>, 47 | ) { 48 | assert(typeof callback === 'function', 'Invalid callback specified.') 49 | 50 | return function (context: 0, kind: number, data: number, contents: number, error: number) { 51 | // Must be a null pointer. 52 | assert(context === 0, 'Callback context must be null.') 53 | console.log({ kind, data }) 54 | const result = callback(coreBindings.copyFromCString(kind) as Arg, coreBindings.copyFromCString(data) as Arg) 55 | if (typeof result.contents === 'string') { 56 | coreBindings.copyToCString(result.contents, contents) 57 | } 58 | if (typeof result.error === 'string') { 59 | coreBindings.copyToCString(result.error, error) 60 | } 61 | } 62 | } 63 | 64 | // calls compile() with args || cb 65 | function runWithCallbacks( 66 | _solJson: SolJson, 67 | coreBindings: CoreBindings, 68 | callbacks?: Callbacks | ReadCallback, 69 | compile?: (...args: Args) => void, 70 | args: Args = [] as unknown as Args, 71 | ) { 72 | if (callbacks) { 73 | assert(typeof callbacks === 'object', 'Invalid callback object specified.') 74 | } else { 75 | callbacks = {} 76 | } 77 | 78 | let readCallback = callbacks.import 79 | if (readCallback === undefined) { 80 | readCallback = function () { 81 | return { 82 | error: 'File import callback not supported', 83 | } 84 | } 85 | } 86 | 87 | let singleCallback 88 | // After 0.6.x multiple kind of callbacks are supported. 89 | let smtSolverCallback = callbacks.smtSolver 90 | if (smtSolverCallback === undefined) { 91 | smtSolverCallback = function () { 92 | return { 93 | error: 'SMT solver callback not supported', 94 | } 95 | } 96 | } 97 | 98 | singleCallback = function (kind: 'source' | 'smt-query', data: string) { 99 | if (kind === 'source') { 100 | return readCallback(data) 101 | } else if (kind === 'smt-query') { 102 | return smtSolverCallback(data) 103 | } else { 104 | assert(false, 'Invalid callback kind specified.') 105 | } 106 | } 107 | 108 | singleCallback = wrapCallbackWithKind<'source' | 'smt-query'>(coreBindings, singleCallback) 109 | 110 | const cb = coreBindings.addFunction(singleCallback, 'viiiii') 111 | let output 112 | try { 113 | args.push(cb) 114 | 115 | // Callback context. 116 | args.push(null) 117 | 118 | output = compile?.(...args) 119 | } finally { 120 | coreBindings.removeFunction(cb) 121 | } 122 | 123 | if (coreBindings.reset) { 124 | // Explicitly free memory. 125 | // 126 | // NOTE: cwrap() of "compile" will copy the returned pointer into a 127 | // Javascript string and it is not possible to call free() on it. 128 | // reset() however will clear up all allocations. 129 | coreBindings.reset() 130 | } 131 | return output 132 | } 133 | -------------------------------------------------------------------------------- /bindings/core.ts: -------------------------------------------------------------------------------- 1 | import type { Alloc, CoreBindings, License, Reset, SolJson } from 'solc/types' 2 | import { bindSolcMethod, bindSolcMethodWithFallbackFunc } from './helpers.ts' 3 | 4 | export function setupCore(solJson: SolJson): CoreBindings { 5 | const core = { 6 | alloc: bindAlloc(solJson), 7 | license: bindLicense(solJson), 8 | version: bindVersion<() => string>(solJson), 9 | reset: bindReset(solJson), 10 | } 11 | 12 | const helpers = { 13 | // @ts-expect-error binding to this 14 | addFunction: unboundAddFunction.bind(this, solJson), 15 | // @ts-expect-error binding to this 16 | removeFunction: unboundRemoveFunction.bind(this, solJson), 17 | // @ts-expect-error binding to this 18 | copyFromCString: unboundCopyFromCString.bind(this, solJson), 19 | // @ts-expect-error binding to this 20 | copyToCString: unboundCopyToCString.bind(this, solJson, core.alloc), 21 | } 22 | 23 | return { 24 | ...core, 25 | ...helpers, 26 | } 27 | } 28 | 29 | /********************** 30 | * Core Functions 31 | **********************/ 32 | 33 | /** 34 | * Returns a binding to the solidity_alloc function. 35 | * 36 | * @param solJson The Emscripten compiled Solidity object. 37 | */ 38 | function bindAlloc(solJson: SolJson) { 39 | const allocBinding = bindSolcMethod( 40 | solJson, 41 | 'solidity_alloc', 42 | 'number', 43 | ['number'], 44 | null, 45 | ) 46 | 47 | return allocBinding 48 | } 49 | 50 | /** 51 | * Returns a binding to the solidity_version method. 52 | * 53 | * @param solJson The Emscripten compiled Solidity object. 54 | */ 55 | function bindVersion(solJson: SolJson) { 56 | return bindSolcMethodWithFallbackFunc( 57 | solJson, 58 | 'solidity_version', 59 | 'string', 60 | [], 61 | 'version', 62 | ) 63 | } 64 | 65 | /** 66 | * Returns a binding to the solidity_license method. 67 | * 68 | * If the current solJson version < 0.4.14 then this will bind an empty function. 69 | * 70 | * @param solJson The Emscripten compiled Solidity object. 71 | */ 72 | function bindLicense(solJson: SolJson) { 73 | return bindSolcMethodWithFallbackFunc( 74 | solJson, 75 | 'solidity_license', 76 | 'string', 77 | [], 78 | 'license', 79 | () => { 80 | }, 81 | ) 82 | } 83 | 84 | /** 85 | * Returns a binding to the solidity_reset method. 86 | * 87 | * @param solJson The Emscripten compiled Solidity object. 88 | */ 89 | function bindReset(solJson: SolJson) { 90 | return bindSolcMethod( 91 | solJson, 92 | 'solidity_reset', 93 | null, 94 | [], 95 | null, 96 | ) 97 | } 98 | 99 | /********************** 100 | * Helpers Functions 101 | **********************/ 102 | 103 | /** 104 | * Copy to a C string. 105 | * 106 | * Allocates memory using solc's allocator. 107 | * 108 | * Before 0.6.0: 109 | * Assuming copyToCString is only used in the context of wrapCallback, solc will free these pointers. 110 | * See https://github.com/ethereum/solidity/blob/v0.5.13/libsolc/libsolc.h#L37-L40 111 | * 112 | * After 0.6.0: 113 | * The duty is on solc-js to free these pointers. We accomplish that by calling `reset` at the end. 114 | * 115 | * @param solJson The Emscripten compiled Solidity object. 116 | * @param alloc The memory allocation function. 117 | * @param str The source string being copied to a C string. 118 | * @param ptr The pointer location where the C string will be set. 119 | */ 120 | function unboundCopyToCString(solJson: SolJson, alloc: Alloc, str: string, ptr: number) { 121 | const length = solJson.lengthBytesUTF8(str) 122 | 123 | const buffer = alloc(length + 1) 124 | 125 | solJson.stringToUTF8(str, buffer, length + 1) 126 | solJson.setValue(ptr, buffer, '*') 127 | 128 | return str 129 | } 130 | 131 | /** 132 | * Wrapper over Emscripten's C String copying function (which can be different 133 | * on different versions). 134 | * 135 | * @param solJson The Emscripten compiled Solidity object. 136 | * @param ptr The pointer location where the C string will be referenced. 137 | */ 138 | function unboundCopyFromCString(solJson: SolJson, ptr: number) { 139 | return solJson.UTF8ToString(ptr) 140 | } 141 | 142 | function unboundAddFunction(solJson: SolJson, func: (...args: unknown[]) => unknown, signature?: string) { 143 | return solJson.addFunction(func, signature) 144 | } 145 | 146 | function unboundRemoveFunction(solJson: SolJson, ptr: number) { 147 | return solJson.removeFunction(ptr) 148 | } 149 | -------------------------------------------------------------------------------- /bindings/helpers.ts: -------------------------------------------------------------------------------- 1 | import { isNil } from '../common.ts' 2 | import type { SolJson } from 'solc/types' 3 | 4 | export function bindSolcMethod( 5 | solJson: SolJson, 6 | method: string, 7 | returnType: string | null, 8 | args: string[], 9 | defaultValue?: unknown, 10 | ): T { 11 | if (isNil(solJson[`_${method}` as keyof typeof solJson]) && defaultValue !== undefined) { 12 | return defaultValue as T 13 | } 14 | 15 | return solJson.cwrap(method, returnType, args) 16 | } 17 | 18 | export function bindSolcMethodWithFallbackFunc( 19 | solJson: SolJson, 20 | method: string, 21 | returnType: string | null, 22 | args: string[], 23 | fallbackMethod: string, 24 | finalFallback: (() => void) | undefined = undefined, 25 | ): T { 26 | const methodFunc = bindSolcMethod(solJson, method, returnType, args, null) 27 | 28 | if (!isNil(methodFunc)) { 29 | return methodFunc as T 30 | } 31 | 32 | return bindSolcMethod(solJson, fallbackMethod, returnType, args, finalFallback) 33 | } 34 | 35 | export function getSupportedMethods(solJson: SolJson) { 36 | return { 37 | licenseSupported: anyMethodExists(solJson, 'solidity_license'), 38 | versionSupported: anyMethodExists(solJson, 'solidity_version'), 39 | allocSupported: anyMethodExists(solJson, 'solidity_alloc'), 40 | resetSupported: anyMethodExists(solJson, 'solidity_reset'), 41 | compileJsonStandardSupported: anyMethodExists(solJson, 'compileStandard', 'solidity_compile'), 42 | } 43 | } 44 | 45 | function anyMethodExists(solJson: SolJson, ...names: string[]) { 46 | return names.some((name) => !isNil(solJson[`_${name}` as keyof typeof solJson])) 47 | } 48 | -------------------------------------------------------------------------------- /bindings/index.ts: -------------------------------------------------------------------------------- 1 | import type { SolJson } from 'solc/types' 2 | import { setupCompile } from './compile.ts' 3 | import { setupCore } from './core.ts' 4 | import { getSupportedMethods } from './helpers.ts' 5 | 6 | export default function setupBindings(solJson: SolJson) { 7 | const coreBindings = setupCore(solJson) 8 | 9 | const compileBindings = setupCompile(solJson, coreBindings) 10 | 11 | const methodFlags = getSupportedMethods(solJson) 12 | 13 | return { 14 | methodFlags, 15 | coreBindings, 16 | compileBindings, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /common.ts: -------------------------------------------------------------------------------- 1 | export const isNil = (value: unknown): value is null => value == null 2 | -------------------------------------------------------------------------------- /common_test.ts: -------------------------------------------------------------------------------- 1 | import { isNil } from './common.ts' 2 | import { expect } from '@std/expect' 3 | import { describe, it } from '@std/testing/bdd' 4 | 5 | describe('common.ts', () => { 6 | it('isNil', () => { 7 | expect(isNil(null)).toBe(true) 8 | expect(isNil(undefined)).toBe(true) 9 | expect(isNil(0)).toBe(false) 10 | expect(isNil('')).toBe(false) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "lint": { 3 | "include": ["./**/*.ts"], 4 | "exclude": ["./**/*.js"], 5 | "rules": { 6 | "exclude": ["no-explicit-any"] 7 | } 8 | }, 9 | "fmt": { 10 | "include": ["./**/*.ts", "./**/*.md", "./**/*.json"], 11 | "exclude": ["./**/*.js"], 12 | "singleQuote": true, 13 | "semiColons": false, 14 | "useTabs": false, 15 | "indentWidth": 2, 16 | "lineWidth": 120 17 | }, 18 | "imports": { 19 | "@std/expect": "jsr:@std/expect@^1.0.13", 20 | "@std/io": "jsr:@std/io@^0.225.2", 21 | "@std/testing": "jsr:@std/testing@^1.0.9", 22 | "solc": "./mod.ts", 23 | "solc/download": "./download.ts", 24 | "solc/types": "./types.ts" 25 | }, 26 | "test": { 27 | "exclude": ["./**/*.js", "examples"] 28 | }, 29 | "tasks": { 30 | "test": "deno test --no-check --allow-net --allow-read --allow-write --coverage=coverage", 31 | "cov": "deno coverage coverage --lcov > coverage.lcov" 32 | }, 33 | "name": "@deno-web3/solc", 34 | "version": "3.0.0", 35 | "license": "MIT", 36 | "exports": { 37 | ".": "./mod.ts", 38 | "./download": "./download.ts", 39 | "./types": "./types.ts" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /deno.lock: -------------------------------------------------------------------------------- 1 | { 2 | "version": "4", 3 | "specifiers": { 4 | "jsr:@std/assert@^1.0.10": "1.0.11", 5 | "jsr:@std/assert@^1.0.11": "1.0.11", 6 | "jsr:@std/bytes@^1.0.5": "1.0.5", 7 | "jsr:@std/expect@^1.0.13": "1.0.13", 8 | "jsr:@std/internal@^1.0.5": "1.0.5", 9 | "jsr:@std/io@~0.225.2": "0.225.2", 10 | "jsr:@std/testing@^1.0.9": "1.0.9", 11 | "npm:@types/node@*": "22.12.0" 12 | }, 13 | "jsr": { 14 | "@std/assert@1.0.11": { 15 | "integrity": "2461ef3c368fe88bc60e186e7744a93112f16fd110022e113a0849e94d1c83c1", 16 | "dependencies": [ 17 | "jsr:@std/internal" 18 | ] 19 | }, 20 | "@std/bytes@1.0.5": { 21 | "integrity": "4465dd739d7963d964c809202ebea6d5c6b8e3829ef25c6a224290fbb8a1021e" 22 | }, 23 | "@std/expect@1.0.13": { 24 | "integrity": "d8e236c7089cd9fcf5e6032f27dadc3db6349d0aee48c15bc71d717bca5baa42", 25 | "dependencies": [ 26 | "jsr:@std/assert@^1.0.11", 27 | "jsr:@std/internal" 28 | ] 29 | }, 30 | "@std/internal@1.0.5": { 31 | "integrity": "54a546004f769c1ac9e025abd15a76b6671ddc9687e2313b67376125650dc7ba" 32 | }, 33 | "@std/io@0.225.2": { 34 | "integrity": "3c740cd4ee4c082e6cfc86458f47e2ab7cb353dc6234d5e9b1f91a2de5f4d6c7", 35 | "dependencies": [ 36 | "jsr:@std/bytes" 37 | ] 38 | }, 39 | "@std/testing@1.0.9": { 40 | "integrity": "9bdd4ac07cb13e7594ac30e90f6ceef7254ac83a9aeaa089be0008f33aab5cd4", 41 | "dependencies": [ 42 | "jsr:@std/assert@^1.0.10", 43 | "jsr:@std/internal" 44 | ] 45 | } 46 | }, 47 | "npm": { 48 | "@types/node@22.12.0": { 49 | "integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==", 50 | "dependencies": [ 51 | "undici-types" 52 | ] 53 | }, 54 | "undici-types@6.20.0": { 55 | "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" 56 | } 57 | }, 58 | "workspace": { 59 | "dependencies": [ 60 | "jsr:@std/expect@^1.0.13", 61 | "jsr:@std/io@~0.225.2", 62 | "jsr:@std/testing@^1.0.9" 63 | ] 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /deps.ts: -------------------------------------------------------------------------------- 1 | export { readerFromStreamReader } from '@std/io/reader-from-stream-reader' 2 | export { copy } from '@std/io/copy' 3 | -------------------------------------------------------------------------------- /download.ts: -------------------------------------------------------------------------------- 1 | import { copy, readerFromStreamReader } from './deps.ts' 2 | 3 | /** 4 | * Downloads Solidity compiler 5 | * @param path download destination 6 | * @param version compiler version. if not specified, latest is downloaded 7 | */ 8 | export const download = async (path = './soljson.cjs', version?: string): Promise => { 9 | console.log(`Fetching releases...`) 10 | const { releases, latestRelease } = 11 | (await fetch('https://binaries.soliditylang.org/emscripten-wasm32/list.json').then((res) => res.json())) as { 12 | releases: Record 13 | latestRelease: string 14 | } 15 | 16 | const jsFile: string = releases[version || latestRelease] 17 | 18 | if (!jsFile) throw new Error(`version ${version} not found`) 19 | 20 | console.log(`Downloading soljson from https://binaries.soliditylang.org/emscripten-wasm32/${jsFile}...`) 21 | 22 | const res = await fetch(`https://binaries.soliditylang.org/emscripten-wasm32/${jsFile}`) 23 | 24 | const rdr = res.body?.getReader() 25 | 26 | if (rdr) { 27 | const r = readerFromStreamReader(rdr) 28 | const f = await Deno.open(path, { create: true, write: true }) 29 | await copy(r, f) 30 | f.close() 31 | } 32 | 33 | return jsFile 34 | } 35 | -------------------------------------------------------------------------------- /download_test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it } from '@std/testing/bdd' 2 | import { expect } from '@std/expect' 3 | import { download } from 'solc/download' 4 | import { exists } from './helpers_test.ts' 5 | 6 | describe('solc/download.ts', () => { 7 | it('downloads latest version to soljson.cjs file', async () => { 8 | await download() 9 | expect(await exists('./soljson.cjs')).toBe(true) 10 | }) 11 | it('downloads latest version to any file path', async () => { 12 | await download('./solc.cjs') 13 | expect(await exists('./solc.cjs')).toBe(true) 14 | await Deno.remove('./solc.cjs') 15 | }) 16 | it('downloads a specific version', async () => { 17 | const jsFile = await download('./soljson.cjs', '0.8.17') 18 | 19 | expect(jsFile).toEqual('solc-emscripten-wasm32-v0.8.17+commit.8df45f5f.js') 20 | }) 21 | it('throws if version does not exist', async () => { 22 | try { 23 | await download('./soljson.cjs', '12.456.789') 24 | } catch (e) { 25 | expect((e as Error).message).toEqual('version 12.456.789 not found') 26 | } 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /egg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://x.nest.land/eggs@0.3.8/src/schema.json", 3 | "name": "solc", 4 | "entry": "./mod.ts", 5 | "description": "💎 Solidity v0.8.7 bindings for Deno", 6 | "homepage": "https://github.com/deno-libs/solc", 7 | "version": "2.1.4", 8 | "releaseType": "patch", 9 | "unstable": false, 10 | "unlisted": false, 11 | "files": [ 12 | "README.md", 13 | "*.ts", 14 | "LICENSE" 15 | ], 16 | "ignore": [], 17 | "checkFormat": false, 18 | "checkTests": false, 19 | "checkInstallation": true, 20 | "check": true, 21 | "checkAll": true, 22 | "repository": "https://github.com/deno-libs/solc" 23 | } 24 | -------------------------------------------------------------------------------- /examples/erc20/ERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity >=0.8.0; 3 | 4 | /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. 5 | /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) 6 | /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) 7 | /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. 8 | abstract contract ERC20 { 9 | /*////////////////////////////////////////////////////////////// 10 | EVENTS 11 | //////////////////////////////////////////////////////////////*/ 12 | 13 | event Transfer(address indexed from, address indexed to, uint256 amount); 14 | 15 | event Approval(address indexed owner, address indexed spender, uint256 amount); 16 | 17 | /*////////////////////////////////////////////////////////////// 18 | METADATA STORAGE 19 | //////////////////////////////////////////////////////////////*/ 20 | 21 | string public name; 22 | 23 | string public symbol; 24 | 25 | uint8 public immutable decimals; 26 | 27 | /*////////////////////////////////////////////////////////////// 28 | ERC20 STORAGE 29 | //////////////////////////////////////////////////////////////*/ 30 | 31 | uint256 public totalSupply; 32 | 33 | mapping(address => uint256) public balanceOf; 34 | 35 | mapping(address => mapping(address => uint256)) public allowance; 36 | 37 | /*////////////////////////////////////////////////////////////// 38 | EIP-2612 STORAGE 39 | //////////////////////////////////////////////////////////////*/ 40 | 41 | uint256 internal immutable INITIAL_CHAIN_ID; 42 | 43 | bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; 44 | 45 | mapping(address => uint256) public nonces; 46 | 47 | /*////////////////////////////////////////////////////////////// 48 | CONSTRUCTOR 49 | //////////////////////////////////////////////////////////////*/ 50 | 51 | constructor( 52 | string memory _name, 53 | string memory _symbol, 54 | uint8 _decimals 55 | ) { 56 | name = _name; 57 | symbol = _symbol; 58 | decimals = _decimals; 59 | 60 | INITIAL_CHAIN_ID = block.chainid; 61 | INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); 62 | } 63 | 64 | /*////////////////////////////////////////////////////////////// 65 | ERC20 LOGIC 66 | //////////////////////////////////////////////////////////////*/ 67 | 68 | function approve(address spender, uint256 amount) public virtual returns (bool) { 69 | allowance[msg.sender][spender] = amount; 70 | 71 | emit Approval(msg.sender, spender, amount); 72 | 73 | return true; 74 | } 75 | 76 | function transfer(address to, uint256 amount) public virtual returns (bool) { 77 | balanceOf[msg.sender] -= amount; 78 | 79 | // Cannot overflow because the sum of all user 80 | // balances can't exceed the max uint256 value. 81 | unchecked { 82 | balanceOf[to] += amount; 83 | } 84 | 85 | emit Transfer(msg.sender, to, amount); 86 | 87 | return true; 88 | } 89 | 90 | function transferFrom( 91 | address from, 92 | address to, 93 | uint256 amount 94 | ) public virtual returns (bool) { 95 | uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. 96 | 97 | if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; 98 | 99 | balanceOf[from] -= amount; 100 | 101 | // Cannot overflow because the sum of all user 102 | // balances can't exceed the max uint256 value. 103 | unchecked { 104 | balanceOf[to] += amount; 105 | } 106 | 107 | emit Transfer(from, to, amount); 108 | 109 | return true; 110 | } 111 | 112 | /*////////////////////////////////////////////////////////////// 113 | EIP-2612 LOGIC 114 | //////////////////////////////////////////////////////////////*/ 115 | 116 | function permit( 117 | address owner, 118 | address spender, 119 | uint256 value, 120 | uint256 deadline, 121 | uint8 v, 122 | bytes32 r, 123 | bytes32 s 124 | ) public virtual { 125 | require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); 126 | 127 | // Unchecked because the only math done is incrementing 128 | // the owner's nonce which cannot realistically overflow. 129 | unchecked { 130 | address recoveredAddress = ecrecover( 131 | keccak256( 132 | abi.encodePacked( 133 | "\x19\x01", 134 | DOMAIN_SEPARATOR(), 135 | keccak256( 136 | abi.encode( 137 | keccak256( 138 | "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" 139 | ), 140 | owner, 141 | spender, 142 | value, 143 | nonces[owner]++, 144 | deadline 145 | ) 146 | ) 147 | ) 148 | ), 149 | v, 150 | r, 151 | s 152 | ); 153 | 154 | require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); 155 | 156 | allowance[recoveredAddress][spender] = value; 157 | } 158 | 159 | emit Approval(owner, spender, value); 160 | } 161 | 162 | function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { 163 | return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); 164 | } 165 | 166 | function computeDomainSeparator() internal view virtual returns (bytes32) { 167 | return 168 | keccak256( 169 | abi.encode( 170 | keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), 171 | keccak256(bytes(name)), 172 | keccak256("1"), 173 | block.chainid, 174 | address(this) 175 | ) 176 | ); 177 | } 178 | 179 | /*////////////////////////////////////////////////////////////// 180 | INTERNAL MINT/BURN LOGIC 181 | //////////////////////////////////////////////////////////////*/ 182 | 183 | function _mint(address to, uint256 amount) internal virtual { 184 | totalSupply += amount; 185 | 186 | // Cannot overflow because the sum of all user 187 | // balances can't exceed the max uint256 value. 188 | unchecked { 189 | balanceOf[to] += amount; 190 | } 191 | 192 | emit Transfer(address(0), to, amount); 193 | } 194 | 195 | function _burn(address from, uint256 amount) internal virtual { 196 | balanceOf[from] -= amount; 197 | 198 | // Cannot underflow because a user's balance 199 | // will never be larger than the total supply. 200 | unchecked { 201 | totalSupply -= amount; 202 | } 203 | 204 | emit Transfer(from, address(0), amount); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /examples/erc20/MyToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8; 3 | 4 | import {ERC20} from "./ERC20.sol"; 5 | 6 | /** 7 | * @dev This is an example contract implementation of NFToken with metadata extension. 8 | */ 9 | contract MyToken is ERC20 { 10 | constructor() ERC20("MyToken", "MYTOKEN", 18) {} 11 | 12 | function mint(address to, uint256 value) external { 13 | _mint(to, value); 14 | } 15 | 16 | function burn(address from, uint256 value) external { 17 | _burn(from, value); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/erc20/mod.ts: -------------------------------------------------------------------------------- 1 | import { wrapper } from 'solc' 2 | import type { Input, Output } from 'solc/types' 3 | import { download } from 'solc/download' 4 | import { exists } from '../../helpers_test.ts' 5 | 6 | if (!(await exists('./soljson.cjs'))) await download() 7 | 8 | const mod = await import('./soljson.cjs') 9 | 10 | const solc = wrapper(mod.default) 11 | 12 | const MyToken = await Deno.readTextFile('./MyToken.sol') 13 | const ERC20 = await Deno.readTextFile('./ERC20.sol') 14 | 15 | const input: Input = { 16 | language: 'Solidity', 17 | sources: { 18 | 'MyToken.sol': { 19 | content: MyToken, 20 | }, 21 | 'ERC20.sol': { 22 | content: ERC20, 23 | }, 24 | }, 25 | settings: { 26 | outputSelection: { 27 | '*': { 28 | '*': ['*'], 29 | }, 30 | }, 31 | }, 32 | } 33 | 34 | const result = JSON.parse(solc.compile(JSON.stringify(input))) as Output 35 | 36 | console.log(result) 37 | -------------------------------------------------------------------------------- /examples/with-library/Example.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8; 3 | 4 | import { LibString } from "./LibString.sol"; 5 | 6 | contract Example { 7 | function stringify(int256 value) external pure returns (string memory) { 8 | return LibString.toString(value); 9 | } 10 | } -------------------------------------------------------------------------------- /examples/with-library/LibString.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | /// @notice Efficient library for creating string representations of integers. 5 | /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol) 6 | /// @author Modified from Solady (https://github.com/Vectorized/solady/blob/main/src/utils/LibString.sol) 7 | library LibString { 8 | function toString(int256 value) internal pure returns (string memory str) { 9 | if (value >= 0) return toString(uint256(value)); 10 | 11 | unchecked { 12 | str = toString(uint256(-value)); 13 | 14 | /// @solidity memory-safe-assembly 15 | assembly { 16 | // Note: This is only safe because we over-allocate memory 17 | // and write the string from right to left in toString(uint256), 18 | // and thus can be sure that sub(str, 1) is an unused memory location. 19 | 20 | let length := mload(str) // Load the string length. 21 | // Put the - character at the start of the string contents. 22 | mstore(str, 45) // 45 is the ASCII code for the - character. 23 | str := sub(str, 1) // Move back the string pointer by a byte. 24 | mstore(str, add(length, 1)) // Update the string length. 25 | } 26 | } 27 | } 28 | 29 | function toString(uint256 value) internal pure returns (string memory str) { 30 | /// @solidity memory-safe-assembly 31 | assembly { 32 | // The maximum value of a uint256 contains 78 digits (1 byte per digit), but we allocate 160 bytes 33 | // to keep the free memory pointer word aligned. We'll need 1 word for the length, 1 word for the 34 | // trailing zeros padding, and 3 other words for a max of 78 digits. In total: 5 * 32 = 160 bytes. 35 | let newFreeMemoryPointer := add(mload(0x40), 160) 36 | 37 | // Update the free memory pointer to avoid overriding our string. 38 | mstore(0x40, newFreeMemoryPointer) 39 | 40 | // Assign str to the end of the zone of newly allocated memory. 41 | str := sub(newFreeMemoryPointer, 32) 42 | 43 | // Clean the last word of memory it may not be overwritten. 44 | mstore(str, 0) 45 | 46 | // Cache the end of the memory to calculate the length later. 47 | let end := str 48 | 49 | // We write the string from rightmost digit to leftmost digit. 50 | // The following is essentially a do-while loop that also handles the zero case. 51 | // prettier-ignore 52 | for { let temp := value } 1 {} { 53 | // Move the pointer 1 byte to the left. 54 | str := sub(str, 1) 55 | 56 | // Write the character to the pointer. 57 | // The ASCII index of the '0' character is 48. 58 | mstore8(str, add(48, mod(temp, 10))) 59 | 60 | // Keep dividing temp until zero. 61 | temp := div(temp, 10) 62 | 63 | // prettier-ignore 64 | if iszero(temp) { break } 65 | } 66 | 67 | // Compute and cache the final total length of the string. 68 | let length := sub(end, str) 69 | 70 | // Move the pointer 32 bytes leftwards to make room for the length. 71 | str := sub(str, 32) 72 | 73 | // Store the string's length at the start of memory allocated for our string. 74 | mstore(str, length) 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /examples/with-library/mod.ts: -------------------------------------------------------------------------------- 1 | import { wrapper } from 'solc' 2 | import type { Input, Output } from 'solc/types' 3 | import { download } from 'solc/download' 4 | import { createRequire } from '../../helpers_test.ts' 5 | import { exists } from '../../helpers_test.ts' 6 | 7 | if (!(await exists('./soljson.cjs'))) await download() 8 | 9 | const require = createRequire(import.meta.url) 10 | const solc = wrapper(require('./soljson.cjs')) 11 | 12 | const Example = await Deno.readTextFile('./Example.sol') 13 | const LibString = await Deno.readTextFile('./LibString.sol') 14 | 15 | const input: Input = { 16 | language: 'Solidity', 17 | sources: { 18 | 'Example.sol': { 19 | content: Example, 20 | }, 21 | 'LibString.sol': { 22 | content: LibString, 23 | }, 24 | }, 25 | 26 | settings: { 27 | outputSelection: { 28 | '*': { 29 | '*': ['*'], 30 | }, 31 | }, 32 | }, 33 | } 34 | 35 | const result = JSON.parse(solc.compile(JSON.stringify(input))) as Output 36 | 37 | console.log(result.contracts['LibString.sol']['LibString'].evm.bytecode) 38 | -------------------------------------------------------------------------------- /helpers_test.ts: -------------------------------------------------------------------------------- 1 | export const exists = async (filename: string): Promise => { 2 | try { 3 | await Deno.stat(filename) 4 | return true 5 | } catch (error) { 6 | if (error instanceof Deno.errors.NotFound) { 7 | return false 8 | } else { 9 | throw error 10 | } 11 | } 12 | } 13 | 14 | export { createRequire } from 'node:module' 15 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deno-web3/solc/84e7bef2b7a1790806ecd13b2aa0a07e8fa4bfaa/logo.png -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | import setupBindings from './bindings/index.ts' 2 | import type { CompileBindings, SolJson, Wrapper } from 'solc/types' 3 | 4 | function compileStandardWrapper(compile: CompileBindings, inputRaw: string, readCallback: unknown) { 5 | return compile.compileStandard(inputRaw, readCallback as number) 6 | } 7 | 8 | /** 9 | * Wrap Solidity compiler into a JS interface 10 | * @param soljson WebAssembly compiler module 11 | */ 12 | export function wrapper(soljson: SolJson): Wrapper { 13 | const { coreBindings, compileBindings } = setupBindings(soljson) 14 | 15 | return { 16 | version: coreBindings.version, 17 | license: coreBindings.license, 18 | // @ts-ignore this stuff 19 | compile: compileStandardWrapper.bind(this, compileBindings), 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /mod_test.ts: -------------------------------------------------------------------------------- 1 | import { beforeAll, describe, it } from '@std/testing/bdd' 2 | import { expect } from '@std/expect' 3 | import { wrapper } from 'solc' 4 | import { createRequire } from './helpers_test.ts' 5 | import { download } from 'solc/download' 6 | import type { Input, Output, Wrapper } from 'solc/types' 7 | 8 | const require = createRequire(import.meta.url) 9 | 10 | globalThis.__dirname = import.meta.dirname! 11 | 12 | const contract = ` 13 | // SPDX-License-Identifier: MIT 14 | pragma solidity >=0.8; 15 | 16 | contract HelloWorld { 17 | string public greet = "Hello World!"; 18 | } 19 | ` 20 | 21 | describe('Wrapper', () => { 22 | let solc: Wrapper 23 | beforeAll(async () => { 24 | await download('./soljson_test.cjs', '0.8.18') 25 | solc = wrapper(require('./soljson_test.cjs')) 26 | }) 27 | it('returns JS interface', () => { 28 | expect(solc.compile).toBeDefined() 29 | expect(solc.version()).toBe('0.8.18+commit.87f61d96.Emscripten.clang') 30 | expect(solc.license()).toContain('Most of the code is licensed under GPLv3 (see below), the license for individual') 31 | }) 32 | it('compiles a Solidity file', () => { 33 | const input: Input = { 34 | language: 'Solidity', 35 | sources: { 36 | 'Hello.sol': { content: contract }, 37 | }, 38 | settings: { 39 | outputSelection: { 40 | '*': { 41 | '*': ['*'], 42 | }, 43 | }, 44 | }, 45 | } 46 | const output: Output = JSON.parse(solc.compile(JSON.stringify(input))) 47 | 48 | expect(output.sources!['Hello.sol'].id).toEqual(0) 49 | expect(output.contracts!['Hello.sol']['HelloWorld'].abi).toEqual([ 50 | { 51 | inputs: [], 52 | name: 'greet', 53 | outputs: [{ internalType: 'string', name: '', type: 'string' }], 54 | stateMutability: 'view', 55 | type: 'function', 56 | }, 57 | ]) 58 | }) 59 | }) 60 | -------------------------------------------------------------------------------- /types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A mapping between libraries and the addresses to which they were deployed. 3 | * 4 | * Containing support for two level configuration, These two level 5 | * configurations can be seen below. 6 | * 7 | * { 8 | * "lib.sol:L1": "0x...", 9 | * "lib.sol:L2": "0x...", 10 | * "lib.sol": {"L3": "0x..."} 11 | * } 12 | */ 13 | export interface LibraryAddresses { 14 | [qualifiedNameOrSourceUnit: string]: string | { [unqualifiedLibraryName: string]: string } 15 | } 16 | 17 | /** 18 | * A mapping between libraries and lists of placeholder instances present in their hex-encoded bytecode. 19 | * For each placeholder its length and the position of the first character is stored. 20 | * 21 | * Each start and length entry will always directly refer to the position in 22 | * binary and not hex-encoded bytecode. 23 | */ 24 | export interface LinkReferences { 25 | [libraryLabel: string]: Array<{ start: number; length: number }> 26 | } 27 | 28 | export interface SolJson { 29 | /** 30 | * Returns a native JavaScript wrapper for a C function. 31 | * 32 | * This is similar to ccall(), but returns a JavaScript function that can be 33 | * reused as many times as needed. The C function can be defined in a C file, 34 | * or be a C-compatible C++ function defined using extern "C" (to prevent 35 | * name mangling). 36 | * 37 | * @param ident The name of the C function to be called. 38 | * 39 | * @param returnType The return type of the function. This can be "number", 40 | * "string" or "array", which correspond to the appropriate JavaScript 41 | * types (use "number" for any C pointer, and "array" for JavaScript arrays 42 | * and typed arrays; note that arrays are 8-bit), or for a void function it 43 | * can be null (note: the JavaScript null value, * not a string containing 44 | * the word “null”). 45 | * 46 | * @param argTypes An array of the types of arguments for the function (if 47 | * there are no arguments, this can be omitted). Types are as in returnType, 48 | * except that array is not supported as there is no way for us to know the 49 | * length of the array). 50 | * 51 | * @returns A JavaScript function that can be used for running the C function. 52 | */ 53 | cwrap(ident: string, returnType: string | null, argTypes: string[]): T 54 | 55 | /** 56 | * Sets a value at a specific memory address at run-time. 57 | * 58 | * Note: 59 | * setValue() and getValue() only do aligned writes and reads. 60 | * 61 | * The type is an LLVM IR type (one of i8, i16, i32, i64, float, double, or 62 | * a pointer type like i8* or just *), not JavaScript types as used in ccall() 63 | * or cwrap(). This is a lower-level operation, and we do need to care what 64 | * specific type is being used. 65 | * 66 | * @param ptr A pointer (number) representing the memory address. 67 | * 68 | * @param value The value to be stored 69 | * 70 | * @param type An LLVM IR type as a string (see “note” above). 71 | * 72 | * @param noSafe Developers should ignore this variable. It is only 73 | * used in SAFE_HEAP compilation mode, where it can help avoid infinite recursion 74 | * in some specialist use cases. 75 | */ 76 | setValue(ptr: number, value: unknown, type: string, noSafe?: boolean): void 77 | 78 | /** 79 | * Given a pointer ptr to a null-terminated UTF8-encoded string in the 80 | * Emscripten HEAP, returns a copy of that string as a JavaScript String 81 | * object. 82 | * 83 | * @param ptr A pointer to a null-terminated UTF8-encoded string in the 84 | * Emscripten HEAP. 85 | * 86 | * @param maxBytesToRead An optional length that specifies the maximum number 87 | * of bytes to read. You can omit this parameter to scan the string until the 88 | * first 0 byte. If maxBytesToRead is passed, and the string at 89 | * [ptr, ptr+maxBytesToReadr) contains a null byte in the middle, then the 90 | * string will cut short at that byte index (i.e. maxBytesToRead will not 91 | * produce a string of exact length [ptr, ptr+maxBytesToRead)) N.B. mixing 92 | * frequent uses of UTF8ToString() with and without maxBytesToRead may throw 93 | * JS JIT optimizations off, so it is worth to consider consistently using 94 | * one style or the other. 95 | */ 96 | UTF8ToString(ptr: number, maxBytesToRead?: number): string 97 | 98 | /** 99 | * v1.38.27: 02/10/2019 (emscripten) 100 | * -------------------- 101 | * - Remove deprecated Pointer_stringify (use UTF8ToString instead). See #8011 102 | * 103 | * @param ptr 104 | * @param length 105 | * @constructor 106 | * 107 | * @deprecated use UTF8ToString instead 108 | */ 109 | // eslint-disable-next-line camelcase 110 | Pointer_stringify(ptr: number, length?: number): string 111 | 112 | /** 113 | * Given a string input return the current length of the given UTF8 bytes. 114 | * Used when performing stringToUTF8 since stringToUTF8 will require at most 115 | * str.length*4+1 bytes of space in the HEAP. 116 | * 117 | * @param str The input string. 118 | */ 119 | lengthBytesUTF8(str: string): number 120 | 121 | /** 122 | * Copies the given JavaScript String object str to the Emscripten HEAP at 123 | * address outPtr, null-terminated and encoded in UTF8 form. 124 | * 125 | * The copy will require at most str.length*4+1 bytes of space in the HEAP. 126 | * You can use the function lengthBytesUTF8() to compute the exact amount 127 | * of bytes (excluding the null terminator) needed to encode the string. 128 | * 129 | * @param str A JavaScript String object. 130 | * 131 | * @param outPtr Pointer to data copied from str, encoded in UTF8 format and 132 | * null-terminated. 133 | * 134 | * @param maxBytesToWrite A limit on the number of bytes that this function 135 | * can at most write out. If the string is longer than this, the output is 136 | * truncated. The outputted string will always be null terminated, even if 137 | * truncation occurred, as long as maxBytesToWrite > 0 138 | */ 139 | stringToUTF8(str: string, outPtr: number, maxBytesToWrite?: number): void 140 | 141 | /** 142 | * Allocates size bytes of uninitialized storage. 143 | * 144 | * If allocation succeeds, returns a pointer that is suitably aligned for any 145 | * object type with fundamental alignment. 146 | * 147 | * @param size number of bytes to allocate 148 | * 149 | * @returns On success, returns the pointer to the beginning of newly 150 | * allocated memory. To avoid a memory leak, the returned pointer must be 151 | * deallocated with free() or realloc(). 152 | */ 153 | _malloc(size: number): number 154 | 155 | /** 156 | * Use addFunction to return an integer value that represents a function 157 | * pointer. Passing that integer to C code then lets it call that value as a 158 | * function pointer, and the JavaScript function you sent to addFunction will 159 | * be called. 160 | * 161 | * when using addFunction on LLVM wasm backend, you need to provide an 162 | * additional second argument, a Wasm function signature string. Each 163 | * character within a signature string represents a type. The first character 164 | * represents the return type of the function, and remaining characters are for 165 | * parameter types. 166 | * 167 | * 'v': void type 168 | * 'i': 32-bit integer type 169 | * 'j': 64-bit integer type (currently does not exist in JavaScript) 170 | * 'f': 32-bit float type 171 | * 'd': 64-bit float type 172 | * 173 | * @param func 174 | * @param signature 175 | */ 176 | addFunction: CoreBindings['addFunction'] 177 | 178 | /** 179 | * Removes an allocated function by the provided function pointer. 180 | * 181 | * @param funcPtr 182 | */ 183 | removeFunction: CoreBindings['removeFunction'] 184 | } 185 | 186 | /************************** 187 | * core binding functions 188 | *************************/ 189 | 190 | /** 191 | * Allocates a chunk of memory of size bytes. 192 | * 193 | * Use this function inside callbacks to allocate data that is to be passed to 194 | * the compiler. You may use solidity_free() or solidity_reset() to free this 195 | * memory again, but it is not required as the compiler takes ownership for any 196 | * data passed to it via callbacks. 197 | * 198 | * This function will return NULL if the requested memory region could not be 199 | * allocated. 200 | * 201 | * @param size The size of bytes to be allocated. 202 | */ 203 | export type Alloc = (size: number) => number 204 | 205 | /** 206 | * Returns the complete license document. 207 | */ 208 | export type License = () => string | undefined 209 | 210 | /** 211 | * This should be called right before each compilation, but not at the end, 212 | * so additional memory can be freed. 213 | */ 214 | export type Reset = () => string 215 | 216 | /** 217 | * Returns the compiler version. 218 | */ 219 | export type Version = () => string 220 | 221 | // compile binding functions 222 | export type ReadCallbackResult = { contents: string } | { error: string } 223 | export type ReadCallback = (path: string) => ReadCallbackResult 224 | export type Callbacks = { [x: string]: ReadCallback } 225 | 226 | /** 227 | * Will attempt to bind into compileStandard before falling back to solidity_compile. 228 | * compileStandard - solidityMaxVersion 0.5.0 229 | * 230 | * @solidityMinVersion 0.4.11 231 | * 232 | * @param input 233 | * @param callbackPtr 234 | * @param contextPtr 235 | */ 236 | export type CompileJsonStandard = (input: string, callbackPtr: number, contextPtr?: number) => string 237 | 238 | /** 239 | * Compile the provided input, using the best case implementation based on the 240 | * current binary. 241 | * 242 | * @param input 243 | * @param readCallback 244 | */ 245 | export type CompileSolidity = (input: string, readCallback?: Callbacks) => string 246 | 247 | export interface CompileBindings { 248 | compileStandard: CompileJsonStandard 249 | } 250 | 251 | export interface CoreBindings { 252 | alloc: Alloc 253 | license: License 254 | reset: Reset 255 | 256 | version: Version 257 | copyFromCString: (ptr: number) => string 258 | copyToCString: (input: string, ptr: number) => string 259 | 260 | addFunction: void>(func: Func, signature?: string) => number 261 | removeFunction: (ptr: number) => void 262 | } 263 | 264 | export interface SupportedMethods { 265 | licenseSupported: boolean 266 | versionSupported: boolean 267 | allocSupported: boolean 268 | resetSupported: boolean 269 | compileJsonSupported: boolean 270 | compileJsonMultiSupported: boolean 271 | compileJsonCallbackSupported: boolean 272 | compileJsonStandardSupported: boolean 273 | } 274 | 275 | export interface Wrapper { 276 | /** 277 | * Returns the complete license document. 278 | */ 279 | license(): string | undefined 280 | 281 | /** 282 | * Returns the compiler version. 283 | */ 284 | version(): string 285 | 286 | /** 287 | * Compile the provided input, using the best case implementation based on the 288 | * current binary. 289 | * 290 | * @param input 291 | * @param readCallback 292 | */ 293 | compile(input: string, readCallback?: Callbacks): string 294 | } 295 | 296 | export interface Input { 297 | language: 'Solidity' | 'Yul' 298 | sources: { [contractName: string]: { content: string } } 299 | settings?: { 300 | outputSelection?: { '*'?: { '*'?: string[] } } 301 | optimizer?: { 302 | enabled?: boolean 303 | runs?: number 304 | } 305 | evmVersion?: string 306 | libraries?: { [libraryName: string]: string } 307 | remappings?: string[] 308 | } 309 | } 310 | 311 | interface ContractABI { 312 | inputs: { internalType: string; name: string; type: string; indexed?: boolean }[] 313 | name: string 314 | outputs: { internalType: string; name: string; type: string }[] 315 | stateMutability: string 316 | type: string 317 | } 318 | 319 | interface DevDoc { 320 | kind: 'dev' 321 | methods: Record 322 | version: number 323 | details: string 324 | } 325 | 326 | interface UserDoc { 327 | kind: 'user' 328 | methods: Record 329 | version: number 330 | } 331 | 332 | interface AST { 333 | nativeSrc: string 334 | nodeType: string 335 | src: string 336 | statements: { body?: AST; name: string; nativeSrc: string; nodeType: string }[] 337 | } 338 | 339 | interface GeneratedSource { 340 | ast: AST 341 | contents: string 342 | id: number 343 | language: 'Yul' 344 | name: `${string}.yul` 345 | } 346 | 347 | interface Bytecode { 348 | linkReferences: LinkReferences 349 | object: string 350 | generatedSources: GeneratedSource[] 351 | } 352 | 353 | interface GasEstimates { 354 | creation: { 355 | codeDepositCost: string 356 | executionCost: string 357 | totalCost: string 358 | } 359 | external: { 360 | [functionName: string]: string 361 | } 362 | } 363 | 364 | interface LegacyAssemblyItem { 365 | begin: number 366 | end: number 367 | name: string 368 | source: number 369 | value: string 370 | } 371 | 372 | interface LegacyAssembly { 373 | '.code': LegacyAssemblyItem[] 374 | '.data': { 375 | '0': { 376 | '.auxdata': string 377 | '.code': LegacyAssemblyItem[] 378 | } 379 | } 380 | sourceList: string[] 381 | } 382 | 383 | interface EVM { 384 | assembly: string 385 | bytecode: Bytecode 386 | deployedBytecode: Bytecode 387 | gasEstimates: GasEstimates 388 | legacyAssembly: LegacyAssembly 389 | methodIdentifiers: Record 390 | } 391 | 392 | interface Ewasm { 393 | wasm: string 394 | } 395 | 396 | interface StorageItem { 397 | astId: number 398 | contract: string 399 | label: string 400 | offset: number 401 | slot: string 402 | type: string 403 | } 404 | 405 | interface StorageType { 406 | encoding: string 407 | label: string 408 | numberOfBytes: string 409 | } 410 | 411 | interface StorageLayout { 412 | storage: StorageItem[] 413 | types: Record 414 | } 415 | 416 | interface Contract { 417 | abi: ContractABI[] 418 | devdoc: DevDoc 419 | evm: EVM 420 | ewasm: Ewasm 421 | metadata: string 422 | storageLayout: StorageLayout 423 | transientStorageLayout?: StorageLayout 424 | userdoc: UserDoc 425 | } 426 | 427 | interface Contracts { 428 | [contractName: string]: { 429 | [contractInstance: string]: Contract 430 | } 431 | } 432 | 433 | interface Sources { 434 | [sourceName: string]: { 435 | id: number 436 | } 437 | } 438 | 439 | export interface Output { 440 | sources: Sources 441 | contracts: Contracts 442 | errors: unknown[] 443 | } 444 | --------------------------------------------------------------------------------