├── .github └── workflows │ ├── ci.yaml │ └── labelled-release.yml ├── .gitignore ├── .npmignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── LICENSE.md ├── README.md ├── bin └── cli ├── docs ├── configuration.md ├── development.md └── opcodes.md ├── examples └── test.teal ├── jest.config.js ├── nodemon.json ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── r.bat ├── src ├── cli.ts ├── index.ts ├── lib │ ├── auto-field.ts │ ├── config.ts │ ├── constants.ts │ ├── context.ts │ ├── convert.ts │ ├── default-value.ts │ ├── error.ts │ ├── execute.ts │ ├── interpreter.ts │ ├── opcode.ts │ ├── opcodes.ts │ ├── opcodes │ │ ├── acct_params_get.ts │ │ ├── add.ts │ │ ├── addr.ts │ │ ├── addw.ts │ │ ├── and.ts │ │ ├── app_global_del.ts │ │ ├── app_global_get.ts │ │ ├── app_global_get_ex.ts │ │ ├── app_global_put.ts │ │ ├── app_local_del.ts │ │ ├── app_local_get.ts │ │ ├── app_local_get_ex.ts │ │ ├── app_local_put.ts │ │ ├── app_opted_in.ts │ │ ├── app_params_get.ts │ │ ├── arg.ts │ │ ├── arg_X.ts │ │ ├── args.ts │ │ ├── assert.ts │ │ ├── asset_holding_get.ts │ │ ├── asset_params_get.ts │ │ ├── balance.ts │ │ ├── band.ts │ │ ├── bdiv.ts │ │ ├── beq.ts │ │ ├── bgt.ts │ │ ├── bgte.ts │ │ ├── binary-operator.ts │ │ ├── binvert.ts │ │ ├── bitlen.ts │ │ ├── blt.ts │ │ ├── blte.ts │ │ ├── bminus.ts │ │ ├── bmod.ts │ │ ├── bmul.ts │ │ ├── bne.ts │ │ ├── bnz.ts │ │ ├── bor.ts │ │ ├── bplus.ts │ │ ├── branch.ts │ │ ├── btoi.ts │ │ ├── byte-binary-operator.ts │ │ ├── byte.ts │ │ ├── byteand.ts │ │ ├── bytec.ts │ │ ├── bytec_X.ts │ │ ├── bytecblock.ts │ │ ├── byteor.ts │ │ ├── bytexor.ts │ │ ├── bz.ts │ │ ├── bzero.ts │ │ ├── callsub.ts │ │ ├── complement.ts │ │ ├── concat.ts │ │ ├── cover.ts │ │ ├── dig.ts │ │ ├── div.ts │ │ ├── divmodw.ts │ │ ├── dup.ts │ │ ├── dup2.ts │ │ ├── ecdsa_pk_decompress.ts │ │ ├── ecdsa_pk_recover.ts │ │ ├── ecdsa_verify.ts │ │ ├── ed25519verify.ts │ │ ├── eq.ts │ │ ├── err.ts │ │ ├── exp.ts │ │ ├── expw.ts │ │ ├── extract.ts │ │ ├── extract3.ts │ │ ├── extractuint_x.ts │ │ ├── gaid.ts │ │ ├── gaids.ts │ │ ├── getbit.ts │ │ ├── getbyte.ts │ │ ├── gload.ts │ │ ├── gloads.ts │ │ ├── global.ts │ │ ├── gt.ts │ │ ├── gte.ts │ │ ├── gtxn.ts │ │ ├── gtxna.ts │ │ ├── gtxnas.ts │ │ ├── gtxns.ts │ │ ├── gtxnsa.ts │ │ ├── gtxnsas.ts │ │ ├── int.ts │ │ ├── intc.ts │ │ ├── intc_X.ts │ │ ├── intcblock.ts │ │ ├── itob.ts │ │ ├── itxn.ts │ │ ├── itxn_begin.ts │ │ ├── itxn_field.ts │ │ ├── itxn_next.ts │ │ ├── itxn_submit.ts │ │ ├── itxna.ts │ │ ├── keccak256.ts │ │ ├── len.ts │ │ ├── load.ts │ │ ├── loads.ts │ │ ├── log.ts │ │ ├── lt.ts │ │ ├── lte.ts │ │ ├── min_balance.ts │ │ ├── minus.ts │ │ ├── mod.ts │ │ ├── mul.ts │ │ ├── mulw.ts │ │ ├── ne.ts │ │ ├── not.ts │ │ ├── or.ts │ │ ├── pop.ts │ │ ├── pushbytes.ts │ │ ├── pushint.ts │ │ ├── retsub.ts │ │ ├── return.ts │ │ ├── select.ts │ │ ├── setbit.ts │ │ ├── setbyte.ts │ │ ├── sha256.ts │ │ ├── sha512_256.ts │ │ ├── shl.ts │ │ ├── shr.ts │ │ ├── sqrt.ts │ │ ├── store.ts │ │ ├── stores.ts │ │ ├── substring.ts │ │ ├── substring3.ts │ │ ├── swap.ts │ │ ├── txn.ts │ │ ├── txna.ts │ │ ├── txnas.ts │ │ ├── uncover.ts │ │ ├── version-pragma.ts │ │ └── xor.ts │ ├── parser.ts │ ├── token.ts │ ├── tokenizer.ts │ └── utils.ts └── test │ ├── auto-create.test.ts │ ├── context.test.ts │ ├── convert.test.ts │ ├── default-value.test.ts │ ├── execute.test.ts │ ├── interpreter.test.ts │ ├── opcode-validation.test.ts │ ├── opcodes.test.ts │ ├── opcodes │ ├── Ecdsa_pk_decompress.test.ts │ ├── Ecdsa_pk_recover.test.ts │ ├── acct_params_get.test.ts │ ├── add.test.ts │ ├── addr.test.ts │ ├── addw.test.ts │ ├── and.test.ts │ ├── app_global_del.test.ts │ ├── app_global_get.test.ts │ ├── app_global_get_ex.test.ts │ ├── app_global_put.test.ts │ ├── app_local_del.test.ts │ ├── app_local_get.test.ts │ ├── app_local_get_ex.test.ts │ ├── app_local_put.test.ts │ ├── app_opted_in.test.ts │ ├── app_params_get.test.ts │ ├── arg.test.ts │ ├── arg_X.test.ts │ ├── args.test.ts │ ├── assert.test.ts │ ├── asset_holding_get.test.ts │ ├── asset_params_get.test.ts │ ├── balance.test.ts │ ├── band.test.ts │ ├── bdiv.test.ts │ ├── beq.test.ts │ ├── bgt.test.ts │ ├── bgte.test.ts │ ├── binvert.test.ts │ ├── bitlen.test.ts │ ├── blt.test.ts │ ├── blte.test.ts │ ├── bminus.test.ts │ ├── bmod.test.ts │ ├── bmul.test.ts │ ├── bne.test.ts │ ├── bnz.test.ts │ ├── bor.test.ts │ ├── bplus.test.ts │ ├── branch.test.ts │ ├── btoi.test.ts │ ├── byte.test.ts │ ├── byteand.test.ts │ ├── bytec.test.ts │ ├── bytec_X.test.ts │ ├── bytecblock.test.ts │ ├── byteor.test.ts │ ├── bytexor.test.ts │ ├── bz.test.ts │ ├── bzero.test.ts │ ├── callsub.test.ts │ ├── complement.test.ts │ ├── concat.test.ts │ ├── cover.test.ts │ ├── dig.test.ts │ ├── div.test.ts │ ├── divmodw.test.ts │ ├── dup.test.ts │ ├── dup2.test.ts │ ├── ecdsa_verify.test.ts │ ├── ed25519verify.test.ts │ ├── eq.test.ts │ ├── exp.test.ts │ ├── expw.test.ts │ ├── extract.test.ts │ ├── extract3.test.ts │ ├── extractuint_x.test.ts │ ├── gaid.test.ts │ ├── gaids.test.ts │ ├── getbit.test.ts │ ├── getbyte.test.ts │ ├── gload.test.ts │ ├── gloads.test.ts │ ├── global.test.ts │ ├── gt.test.ts │ ├── gte.test.ts │ ├── gtxn.test.ts │ ├── gtxna.test.ts │ ├── gtxnas.test.ts │ ├── gtxns.test.ts │ ├── gtxnsa.test.ts │ ├── gtxnsas.test.ts │ ├── int.test.ts │ ├── intc.test.ts │ ├── intc_X.test.ts │ ├── intcblock.test.ts │ ├── itob.test.ts │ ├── itxn.test.ts │ ├── itxn_begin.test.ts │ ├── itxn_field.test.ts │ ├── itxn_next.test.ts │ ├── itxn_submit.test.ts │ ├── itxna.test.ts │ ├── keccak256.test.ts │ ├── len.test.ts │ ├── load.test.ts │ ├── loads.test.ts │ ├── log.test.ts │ ├── lt.test.ts │ ├── lte.test.ts │ ├── min_balance.test.ts │ ├── minus.test.ts │ ├── mod.test.ts │ ├── mul.test.ts │ ├── mulw.test.ts │ ├── ne.test.ts │ ├── not.test.ts │ ├── or.test.ts │ ├── pop.test.ts │ ├── pushbytes.test.ts │ ├── pushint.test.ts │ ├── retsub.test.ts │ ├── return.test.ts │ ├── select.test.ts │ ├── setbit.test.ts │ ├── setbyte.test.ts │ ├── sha256.test.ts │ ├── sha512_256.test.ts │ ├── shl.test.ts │ ├── shr.test.ts │ ├── sqrt.test.ts │ ├── store.test.ts │ ├── stores.test.ts │ ├── substring.test.ts │ ├── substring3.test.ts │ ├── swap.test.ts │ ├── txn.test.ts │ ├── txna.test.ts │ ├── txnas.test.ts │ ├── uncover.test.ts │ └── xor.test.ts │ ├── parser.test.ts │ └── tokenizer.test.ts ├── stamp-version.js ├── tsconfig.json └── tslint.json /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | timeout-minutes: 5 12 | 13 | steps: 14 | 15 | # Checks-out your repository under $GITHUB_WORKSPACE. 16 | - uses: actions/checkout@v2 17 | 18 | # Installs Node.js. 19 | - uses: actions/setup-node@v1 20 | with: 21 | node-version: 16 22 | 23 | - name: Install dependencies 24 | run: npm ci 25 | 26 | - name: Run tests 27 | run: npm test 28 | 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | build/ 61 | *.bak 62 | tsconfig.tsbuildinfo 63 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | node_modules/ 3 | .vscode/ 4 | .gitignore 5 | *.log 6 | r.bat 7 | test/ 8 | build/test/ 9 | todo.txt 10 | .nyc_output/ 11 | tsconfig.json 12 | tslint.json 13 | nodemon.json 14 | jest.config.js 15 | .env 16 | tsconfig.tsbuildinfo 17 | examples/ 18 | .github/ 19 | stamp-version.js 20 | docs/ -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules\\typescript\\lib" 3 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "typescript", 8 | "tsconfig": "tsconfig.json", 9 | "group": { 10 | "kind": "build", 11 | "isDefault": true 12 | } 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Optio Labs 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 | -------------------------------------------------------------------------------- /bin/cli: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | require('../build/cli.js'); -------------------------------------------------------------------------------- /docs/development.md: -------------------------------------------------------------------------------- 1 | # Development doc for the TEAL interpeter 2 | 3 | ## Run in development 4 | 5 | Clone this repo. 6 | 7 | Install dependencies: 8 | 9 | ```bash 10 | cd teal-interpreter 11 | npm install 12 | ``` 13 | 14 | Run once off (with example/test.teal) as input: 15 | 16 | ```bash 17 | npm start 18 | ``` 19 | 20 | Or run in watch mode with live reload: 21 | 22 | ```bash 23 | npm run start:dev 24 | ``` 25 | 26 | Full command: 27 | 28 | ```bash 29 | npx ts-node ./src/cli.ts ./examples/test.teal 30 | ``` 31 | 32 | ## Testing 33 | 34 | Install dependencies as above, then run tests: 35 | 36 | ```bash 37 | npm test 38 | ``` 39 | 40 | Or run tests in watch mode with live reload: 41 | 42 | ```bash 43 | npm run test:watch 44 | ``` 45 | 46 | ## Deployment 47 | 48 | To deploy a new version of the TEAL interpretter simply tag the commit for the new release with the version number in the following form: 49 | 50 | ```bash 51 | v0.0.5 52 | ``` 53 | 54 | Don't forget to add the `v` to the tag, this is how the deployment pipeline knows the tag is a version (and not some other tag). 55 | 56 | Now push tags: 57 | 58 | ``` 59 | git push --tags 60 | ``` 61 | 62 | The updated version will deploy automatically to npm (provided the automated tests pass). -------------------------------------------------------------------------------- /examples/test.teal: -------------------------------------------------------------------------------- 1 | int 1 2 | int 2 3 | int 3 4 | pop -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | modulePathIgnorePatterns: [ 5 | "/build" 6 | ] 7 | }; -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": "ts", 4 | "ignore": ["src/**/*.test.ts"], 5 | "exec": "npx ts-node ./src/cli.ts ./examples/test.teal --code-coverage" 6 | } -------------------------------------------------------------------------------- /r.bat: -------------------------------------------------------------------------------- 1 | npm run %1 -------------------------------------------------------------------------------- /src/cli.ts: -------------------------------------------------------------------------------- 1 | import * as minimist from "minimist"; 2 | import { execute, ITealInterpreterConfig } from "."; 3 | import * as fs from "fs-extra"; 4 | 5 | async function main(): Promise { 6 | const argv = minimist(process.argv.slice(2)); 7 | if (argv._.length === 0) { 8 | console.log(`Usage: teal `); 9 | process.exit(1); 10 | } 11 | 12 | const tealFilePath = argv._[0]; 13 | const configFilePath = tealFilePath + ".json"; 14 | const configExists = await fs.pathExists(configFilePath); 15 | let config: ITealInterpreterConfig = {}; 16 | 17 | if (configExists) { 18 | console.log(`Loading config: ${configFilePath}`); 19 | 20 | config = JSON.parse(await fs.readFile(configFilePath, "utf8")); 21 | } 22 | 23 | config.showCodeCoverage = !!argv["code-coverage"]; 24 | 25 | const tealCode = await fs.readFile(tealFilePath, "utf8"); 26 | const result = await execute(tealCode, config); 27 | 28 | console.log(`== RESULT ==`); 29 | console.log(`Stack:`); 30 | console.log(result.stack); 31 | } 32 | 33 | main() 34 | .catch(err => { 35 | console.error(`Failed:`); 36 | console.error(err && err.stack || err); 37 | }); -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export { parse, IParseResult } from "./lib/parser"; 3 | export { IOpcode } from "./lib/opcode"; 4 | export { execute } from "./lib/execute"; 5 | export { ITypedValue, ValueType, IAccount, IExecutionContext, isTypedValue } from "./lib/context"; 6 | export { ITealInterpreterConfig, ITable, ValueDef, IAccountDef } from "./lib/config"; 7 | export { ITealInterpreter, TealInterpreter } from "./lib/interpreter"; 8 | export { loadValue } from "./lib/convert"; -------------------------------------------------------------------------------- /src/lib/auto-field.ts: -------------------------------------------------------------------------------- 1 | 2 | import { ITypedValue } from "./context"; 3 | 4 | // 5 | // Automatically creates a missing field in the context. 6 | // 7 | export function autoCreateField(container: any, fieldPath: string, value: ITypedValue): void { 8 | const parts = fieldPath.split("."); 9 | const fieldName = parts.pop()!; 10 | let working = container; 11 | for (const part of parts) { 12 | if (working[part] === undefined) { 13 | working[part] = {}; 14 | } 15 | working = working[part]; 16 | } 17 | 18 | working[fieldName] = value; 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Named integer constants. 3 | // 4 | // https://developer.algorand.org/docs/get-details/dapps/avm/teal/specification/#named-integer-constants 5 | // 6 | 7 | export const constants: any = { 8 | // https://developer.algorand.org/docs/get-details/dapps/avm/teal/specification/#oncomplete 9 | NoOp: 0, 10 | OptIn: 1, 11 | CloseOut: 2, 12 | ClearState: 3, 13 | UpdateApplication: 4, 14 | DeleteApplication: 5, 15 | 16 | // https://developer.algorand.org/docs/get-details/dapps/avm/teal/specification/#typeenum-constants 17 | pay: 1, 18 | keyreg: 2, 19 | acfg: 3, 20 | axfer: 4, 21 | afrz: 5, 22 | appl: 6, 23 | }; -------------------------------------------------------------------------------- /src/lib/default-value.ts: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Finds the default configuration value for a particular path give an spec. 4 | // 5 | export function getDefaultValue(fieldPath: string, defaultValueSpec: any): any { 6 | let working: any = defaultValueSpec; 7 | const parts = fieldPath.split("."); 8 | const walkPath: string[] = []; 9 | 10 | for (const part of parts) { 11 | if (working[part] !== undefined) { 12 | walkPath.push(part); 13 | working = working[part]; 14 | continue; 15 | } 16 | 17 | if (working["*"] !== undefined) { 18 | walkPath.push("*"); 19 | working = working["*"]; 20 | continue; 21 | } 22 | 23 | working = undefined; 24 | break; 25 | } 26 | 27 | if (working !== undefined) { 28 | if (working.new !== undefined) { 29 | working = working.new; 30 | } 31 | 32 | if (typeof(working) === "function") { 33 | return working(); 34 | } 35 | else { 36 | return working; 37 | } 38 | } 39 | 40 | throw new Error(`Failed to find default for "${fieldPath}", to the default value spec. Walked the following path "${walkPath.join(".")}, but failed to find a constructor.`); 41 | } 42 | 43 | -------------------------------------------------------------------------------- /src/lib/error.ts: -------------------------------------------------------------------------------- 1 | import { IToken } from "./token"; 2 | 3 | // 4 | // Represents an error that can be thrown by the interpreter. 5 | // 6 | export class InterpreterError extends Error { 7 | 8 | // 9 | // The instruction that caused the error, if any. 10 | // 11 | public readonly instruction: IToken | undefined; 12 | 13 | constructor(msg: string, instruction?: IToken) { 14 | super(msg); 15 | 16 | this.instruction = instruction; 17 | } 18 | } -------------------------------------------------------------------------------- /src/lib/execute.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext, ITealInterpreterConfig, TealInterpreter } from ".."; 2 | 3 | // 4 | // Executes TEAL code and returns a result. 5 | // 6 | export async function execute(tealCode: string, config?: ITealInterpreterConfig): Promise { 7 | 8 | const interpreter = new TealInterpreter(); 9 | interpreter.load(tealCode); 10 | interpreter.configure(config); 11 | 12 | try { 13 | await interpreter.run(); 14 | } 15 | finally { 16 | if (config?.showCodeCoverage) { 17 | interpreter.printCodeCoverage(); 18 | } 19 | } 20 | 21 | return interpreter.context; 22 | } -------------------------------------------------------------------------------- /src/lib/opcodes/acct_params_get.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | import { decodeAddress } from "../convert"; 4 | import { InterpreterError } from "../error"; 5 | 6 | export class AccountParamsGet extends Opcode { 7 | 8 | // 9 | // The name of the field to get from the asset. 10 | // 11 | private fieldName!: string; 12 | 13 | validateOperand() { 14 | super.validateOperand(); 15 | 16 | this.fieldName = this.token.operands[0]; 17 | } 18 | 19 | async execute(context: IExecutionContext) { 20 | 21 | const accountName = decodeAddress(this.popBytes(context)); 22 | const key = `accountParams.${accountName}.${this.fieldName}`; 23 | const value = await context.requestValue(key); 24 | if (value === undefined) { 25 | const msg = `Value "${key}" not found for account ${accountName}.`; 26 | throw new InterpreterError(msg, this.token); 27 | } 28 | 29 | context.stack.push(value); 30 | this.pushInt(context, value.type === "bigint" && Number(value.value) <= 0 ? BigInt(0) : BigInt(1)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/lib/opcodes/add.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Add extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, this.a + this.b); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/addr.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | import { encodeAddress } from "../convert"; 4 | 5 | export class Addr extends Opcode { 6 | 7 | // 8 | // The value to be pushed on the stack. 9 | // 10 | private value!: string; 11 | 12 | validateOperand(): void { 13 | super.validateOperand(); 14 | 15 | this.value = this.token.operands[0]; 16 | } 17 | 18 | execute(context: IExecutionContext): void { 19 | this.pushBytes(context, encodeAddress(this.value)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/lib/opcodes/addw.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Addw extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | let result = this.a + this.b; 8 | const maxUint64 = BigInt("0xFFFFFFFFFFFFFFFF"); 9 | 10 | if (result > maxUint64) { 11 | result -= maxUint64; 12 | this.pushInt(context, BigInt(1)); 13 | this.pushInt(context, result - BigInt(1)); 14 | } 15 | else { 16 | this.pushInt(context, BigInt(0)); 17 | this.pushInt(context, result); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/lib/opcodes/and.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class And extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, (this.a != BigInt(0) && this.b != BigInt(0)) ? BigInt(1) : BigInt(0)); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/app_global_del.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class AppGlobalDel extends Opcode { 5 | 6 | execute(context: IExecutionContext) { 7 | const globalName = Buffer.from(this.popBytes(context)).toString(); 8 | const appGlobals = context.appGlobals["0"]; 9 | if (appGlobals !== undefined) { 10 | delete appGlobals[globalName]; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/opcodes/app_global_get.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class AppGlobalGet extends Opcode { 5 | 6 | async execute(context: IExecutionContext) { 7 | const globalName = Buffer.from(this.popBytes(context)).toString(); 8 | const value = await context.requireValue(`appGlobals.0.${globalName}`, this.token); 9 | context.stack.push(value); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/lib/opcodes/app_global_get_ex.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class AppGlobalGetEx extends Opcode { 5 | 6 | async execute(context: IExecutionContext) { 7 | const globalName = Buffer.from(this.popBytes(context)).toString(); 8 | const appId = Number(this.popInt(context)); 9 | const value = await context.requestValue(`appGlobals.${appId}.${globalName}`); 10 | if (value === undefined) { 11 | // The value doesn't exist. 12 | this.pushInt(context, BigInt(0)); 13 | this.pushInt(context, BigInt(0)); 14 | return; 15 | } 16 | 17 | context.stack.push(value); 18 | this.pushInt(context, BigInt(1)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/lib/opcodes/app_global_put.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class AppGlobalPut extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | const value = context.stack.pop()!; 8 | const globalName = Buffer.from(this.popBytes(context)).toString(); 9 | let appGlobals = context.appGlobals["0"]; 10 | if (appGlobals === undefined) { 11 | appGlobals = context.appGlobals["0"] = {}; 12 | } 13 | 14 | appGlobals[globalName] = value; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/opcodes/app_local_del.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IAccount, IExecutionContext } from "../context"; 3 | import { decodeAddress } from "../convert"; 4 | 5 | export class AppLocalDel extends Opcode { 6 | 7 | async execute(context: IExecutionContext) { 8 | const localName = Buffer.from(this.popBytes(context)).toString(); 9 | const accountName = decodeAddress(this.popBytes(context)); 10 | const account = await context.requireValue(`accounts.${accountName}`, this.token); 11 | const appLocals = account.appLocals["0"]; 12 | if (appLocals !== undefined) { 13 | delete appLocals[localName]; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/opcodes/app_local_get.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | import { decodeAddress } from "../convert"; 4 | 5 | export class AppLocalGet extends Opcode { 6 | 7 | async execute(context: IExecutionContext) { 8 | const localName = Buffer.from(this.popBytes(context)).toString(); 9 | const accountName = decodeAddress(this.popBytes(context)); 10 | const value = await context.requestValue(`accounts.${accountName}.appLocals.0.${localName}`); 11 | if (value === undefined) { 12 | this.pushInt(context, BigInt(0)); // Value does not exist. 13 | return; 14 | } 15 | 16 | context.stack.push(value); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/opcodes/app_local_get_ex.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | import { decodeAddress } from "../convert"; 4 | 5 | export class AppLocalGetEx extends Opcode { 6 | 7 | async execute(context: IExecutionContext) { 8 | const localName = Buffer.from(this.popBytes(context)).toString(); 9 | const appId = Number(this.popInt(context)).toString(); 10 | const accountName = decodeAddress(this.popBytes(context)); 11 | const value = await context.requestValue(`accounts.${accountName}.appLocals.${appId}.${localName}`); 12 | if (value !== undefined) { 13 | context.stack.push(value); 14 | this.pushInt(context, BigInt(1)); 15 | return; 16 | } 17 | 18 | // Not found. 19 | this.pushInt(context, BigInt(0)); 20 | this.pushInt(context, BigInt(0)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/opcodes/app_local_put.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IAccount, IExecutionContext } from "../context"; 3 | import { decodeAddress } from "../convert"; 4 | 5 | export class AppLocalPut extends Opcode { 6 | 7 | async execute(context: IExecutionContext): Promise { 8 | const value = context.stack.pop()!; 9 | const localName = Buffer.from(this.popBytes(context)).toString(); 10 | const accountName = decodeAddress(this.popBytes(context)); 11 | const account = await context.requireValue(`accounts.${accountName}`, this.token); 12 | let appLocals = account.appLocals["0"]; 13 | if (appLocals === undefined) { 14 | appLocals = account.appLocals["0"] = {}; 15 | } 16 | 17 | appLocals[localName] = value; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/opcodes/app_opted_in.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IAccount, IExecutionContext } from "../context"; 3 | import { decodeAddress } from "../convert"; 4 | 5 | export class AppOptedIn extends Opcode { 6 | 7 | async execute(context: IExecutionContext) { 8 | const appId = Number(this.popInt(context)); 9 | const accountName = decodeAddress(this.popBytes(context)); 10 | const account = await context.requireValue(`accounts.${accountName}`, this.token); 11 | if (account.appsOptedIn.includes(appId.toString())) { 12 | this.pushInt(context, BigInt(1)); 13 | } 14 | else { 15 | this.pushInt(context, BigInt(0)); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/opcodes/app_params_get.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class AppParamsGet extends Opcode { 5 | 6 | // 7 | // The name of the field to get from the asset. 8 | // 9 | private fieldName!: string; 10 | 11 | validateOperand() { 12 | super.validateOperand(); 13 | 14 | this.fieldName = this.token.operands[0]; 15 | } 16 | 17 | async execute(context: IExecutionContext) { 18 | 19 | const appId = this.popInt(context).toString(); 20 | const value = await context.requestValue(`appParams.${appId}.${this.fieldName}`); 21 | if (value === undefined) { 22 | // App not found. 23 | this.pushInt(context, BigInt(0)); 24 | this.pushInt(context, BigInt(0)); 25 | return; 26 | } 27 | 28 | context.stack.push(value); 29 | this.pushInt(context, BigInt(1)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/lib/opcodes/arg.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Arg extends Opcode { 5 | 6 | // 7 | // The parsed index operand. 8 | // 9 | private argIndex!: number; 10 | 11 | validateOperand() { 12 | super.validateOperand(); 13 | 14 | this.argIndex = Number(this.parseIntOperand(0)); 15 | } 16 | 17 | async execute(context: IExecutionContext) { 18 | 19 | const value = await context.requireValue(`args.${this.argIndex}`, this.token); 20 | context.stack.push(value); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/opcodes/arg_X.ts: -------------------------------------------------------------------------------- 1 | import { IToken } from "../token"; 2 | import { Opcode } from "../opcode"; 3 | import { IExecutionContext } from "../context"; 4 | import { IOpcodeDef } from "../opcodes"; 5 | 6 | export class Arg_X extends Opcode { 7 | 8 | constructor(token: IToken, opcodeDef: IOpcodeDef, private constantIndex: number) { 9 | super(token, opcodeDef); 10 | } 11 | 12 | async execute(context: IExecutionContext) { 13 | const value = await context.requireValue(`args.${this.constantIndex}`, this.token); 14 | context.stack.push(value); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/opcodes/args.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Args extends Opcode { 5 | 6 | // 7 | // The parsed index operand. 8 | // 9 | private argIndex!: number; 10 | 11 | validateContext(context: IExecutionContext) { 12 | super.validateContext(context); 13 | 14 | this.argIndex = Number(this.popInt(context)); 15 | } 16 | 17 | async execute(context: IExecutionContext) { 18 | 19 | const value = await context.requireValue(`args.${this.argIndex}`, this.token); 20 | context.stack.push(value); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/opcodes/assert.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | import { stringify } from "../utils"; 4 | 5 | export class Assert extends Opcode { 6 | 7 | execute(context: IExecutionContext): void { 8 | const value = context.stack.pop()!; 9 | if (value.type !== "bigint") { 10 | this.raiseError(`assert expects one bigint on the stack, instead found ${value.type} (line: ${this.token.lineNo})\r\nStack:\r\n${stringify(context.stack)}`); 11 | } 12 | 13 | if (value.value as bigint === BigInt(0)) { 14 | this.raiseError(`assert expects a non-zero value on the stack (line: ${this.token.lineNo})\r\nStack:\r\n${stringify(context.stack)}`); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/opcodes/asset_holding_get.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | import { decodeAddress } from "../convert"; 4 | 5 | export class AssetHoldingGet extends Opcode { 6 | 7 | // 8 | // The name of the field to get from the asset. 9 | // 10 | private fieldName!: string; 11 | 12 | validateOperand() { 13 | super.validateOperand(); 14 | 15 | this.fieldName = this.token.operands[0]; 16 | } 17 | 18 | async execute(context: IExecutionContext) { 19 | 20 | const assetId = Number(this.popInt(context)).toString(); 21 | const accountName = decodeAddress(this.popBytes(context)); 22 | const value = await context.requestValue(`accounts.${accountName}.assetHoldings.${assetId}.${this.fieldName}`); 23 | if (value === undefined) { 24 | // Asset not found. 25 | this.pushInt(context, BigInt(0)); 26 | this.pushInt(context, BigInt(0)); 27 | return; 28 | } 29 | 30 | context.stack.push(value); 31 | this.pushInt(context, BigInt(1)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/lib/opcodes/asset_params_get.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class AssetParamsGet extends Opcode { 5 | 6 | // 7 | // The name of the field to get from the asset. 8 | // 9 | private fieldName!: string; 10 | 11 | validateOperand() { 12 | super.validateOperand(); 13 | 14 | this.fieldName = this.token.operands[0]; 15 | } 16 | 17 | async execute(context: IExecutionContext) { 18 | 19 | const assetId = this.popInt(context).toString(); 20 | const value = await context.requestValue(`assetParams.${assetId}.${this.fieldName}`); 21 | if (value === undefined) { 22 | // Asset not found. 23 | this.pushInt(context, BigInt(0)); 24 | this.pushInt(context, BigInt(0)); 25 | return; 26 | } 27 | 28 | context.stack.push(value); 29 | this.pushInt(context, BigInt(1)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/lib/opcodes/balance.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext, makeBigInt } from "../context"; 3 | import { decodeAddress } from "../convert"; 4 | 5 | export class Balance extends Opcode { 6 | 7 | async execute(context: IExecutionContext) { 8 | const accountName = decodeAddress(this.popBytes(context)); 9 | const value = await context.requireValue(`accounts.${accountName}.balance`, this.token); 10 | context.stack.push(makeBigInt(BigInt(value))); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/opcodes/band.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Band extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, this.a & this.b); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/bdiv.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { ByteBinary } from "./byte-binary-operator"; 3 | 4 | export class BDiv extends ByteBinary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushIntAsBytes(context, this.intA / this.intB); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/beq.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { ByteBinary } from "./byte-binary-operator"; 3 | 4 | export class Beq extends ByteBinary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, this.intA === this.intB ? BigInt(1) : BigInt(0)); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/bgt.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { ByteBinary } from "./byte-binary-operator"; 3 | 4 | export class Bgt extends ByteBinary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, this.intA > this.intB ? BigInt(1) : BigInt(0)); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/bgte.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { ByteBinary } from "./byte-binary-operator"; 3 | 4 | export class Bgte extends ByteBinary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, this.intA >= this.intB ? BigInt(1) : BigInt(0)); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/binary-operator.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export abstract class Binary extends Opcode { 5 | 6 | // 7 | // Arguments for the binary operator popped form the compute stack. 8 | // 9 | protected a!: bigint; 10 | protected b!: bigint; 11 | 12 | validateContext(context: IExecutionContext) { 13 | super.validateContext(context); 14 | 15 | this.b = this.popInt(context); 16 | this.a = this.popInt(context); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/opcodes/binvert.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class BInvert extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushBytes(context, this.popBytes(context).map(v => 255 - v)); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/bitlen.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Opcode } from "../opcode"; 3 | 4 | export class Bitlen extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | 8 | const stackTop = context.stack.pop()!; 9 | const value = stackTop.value; 10 | if (stackTop.type === "bigint") { 11 | this.pushInt(context, (value === BigInt(0)) ? BigInt(0) : BigInt(value.toString(2).length)); 12 | } 13 | else { 14 | const array = value as Uint8Array; 15 | if (array.length > 0) { 16 | const bits = array[0].toString(2); 17 | if (bits === "0") { 18 | this.pushInt(context, BigInt(0)); 19 | } 20 | else { 21 | this.pushInt(context, BigInt((array.length - 1) * 8 + array[0].toString(2).length)); 22 | } 23 | } 24 | else { 25 | this.pushInt(context, BigInt(0)); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/lib/opcodes/blt.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { ByteBinary } from "./byte-binary-operator"; 3 | 4 | export class Blt extends ByteBinary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, this.intA < this.intB ? BigInt(1) : BigInt(0)); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/blte.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { ByteBinary } from "./byte-binary-operator"; 3 | 4 | export class Blte extends ByteBinary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, this.intA <= this.intB ? BigInt(1) : BigInt(0)); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/bminus.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { ByteBinary } from "./byte-binary-operator"; 3 | 4 | export class BMinus extends ByteBinary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushIntAsBytes(context, this.intA - this.intB); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/bmod.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { ByteBinary } from "./byte-binary-operator"; 3 | 4 | export class BMod extends ByteBinary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushIntAsBytes(context, this.intA % this.intB); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/bmul.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { ByteBinary } from "./byte-binary-operator"; 3 | 4 | export class BMul extends ByteBinary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushIntAsBytes(context, this.intA * this.intB); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/bne.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { ByteBinary } from "./byte-binary-operator"; 3 | 4 | export class Bne extends ByteBinary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, this.intA !== this.intB ? BigInt(1) : BigInt(0)); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/bnz.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Bnz extends Opcode { 5 | 6 | // 7 | // The name of the target to branch to. 8 | // 9 | private targetName!: string; 10 | 11 | // 12 | // The value to branch on. 13 | // 14 | private condition!: bigint; 15 | 16 | validateOperand() { 17 | super.validateOperand(); 18 | 19 | this.targetName = this.token.operands[0]; 20 | } 21 | 22 | validateContext(context: IExecutionContext) { 23 | const branchTarget = context.branchTargets[this.targetName]; 24 | if (branchTarget === undefined) { 25 | throw new Error(`Failed to find branch target ${this.targetName}.`); 26 | } 27 | 28 | this.condition = this.popInt(context); 29 | } 30 | 31 | execute(context: IExecutionContext): number | void { 32 | if (this.condition !== BigInt(0)) { 33 | // Branch if not zero. 34 | return context.branchTargets[this.targetName].targetInstructionIndex; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/lib/opcodes/bor.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Bor extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, this.a | this.b); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/bplus.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { ByteBinary } from "./byte-binary-operator"; 3 | 4 | export class BPlus extends ByteBinary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushIntAsBytes(context, this.intA + this.intB); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/branch.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Branch extends Opcode { 5 | 6 | // 7 | // The name of the target to branch to. 8 | // 9 | private targetName!: string; 10 | 11 | validateOperand() { 12 | super.validateOperand(); 13 | 14 | this.targetName = this.token.operands[0]; 15 | } 16 | 17 | validateContext(context: IExecutionContext) { 18 | const branchTarget = context.branchTargets[this.targetName]; 19 | if (branchTarget === undefined) { 20 | throw new Error(`Failed to find branch target ${this.targetName}.`); 21 | } 22 | } 23 | 24 | execute(context: IExecutionContext): number { 25 | return context.branchTargets[this.targetName].targetInstructionIndex; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/lib/opcodes/btoi.ts: -------------------------------------------------------------------------------- 1 | import { decodeUint64 } from "algosdk"; 2 | import { IExecutionContext } from "../context"; 3 | import { Opcode } from "../opcode"; 4 | 5 | export class Btoi extends Opcode { 6 | 7 | execute(context: IExecutionContext): void { 8 | const value = this.popBytes(context); 9 | if (value.length > 8) { 10 | throw new Error(`Input byte array is too long at ${value.length}, expected length is between 0 and 8.`); 11 | } 12 | else if (value.length === 0) { 13 | this.pushInt(context, BigInt(0)); 14 | } 15 | else { 16 | this.pushInt(context, decodeUint64(value, "bigint")); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/opcodes/byte-binary-operator.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export abstract class ByteBinary extends Opcode { 5 | 6 | // 7 | // Arguments for the binary operator popped from the compute stack. 8 | // 9 | protected bytesA!: Uint8Array; 10 | protected bytesB!: Uint8Array; 11 | protected intA!: bigint; 12 | protected intB!: bigint; 13 | 14 | validateContext(context: IExecutionContext) { 15 | super.validateContext(context); 16 | 17 | this.bytesB = this.popBytes(context); 18 | this.intB = this.bytesToInt(this.bytesB); 19 | 20 | this.bytesA = this.popBytes(context); 21 | this.intA = this.bytesToInt(this.bytesA); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/lib/opcodes/byteand.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { ByteBinary } from "./byte-binary-operator"; 3 | 4 | export class ByteAnd extends ByteBinary { 5 | 6 | execute(context: IExecutionContext): void { 7 | const result = this.intA & this.intB; 8 | 9 | // https://developer.algorand.org/docs/reference/teal/specification/#arithmetic-logic-and-cryptographic-operations 10 | const resultBytes = this.intToBytes(result); 11 | const max = Math.max(this.bytesA.length, this.bytesB.length); 12 | const padded = new Uint8Array(Math.max(0, max - resultBytes.length)).fill(0); 13 | const merged = new Uint8Array(max); 14 | merged.set(padded); 15 | merged.set(resultBytes, padded.length); 16 | this.pushBytes(context, merged); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/opcodes/bytec.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Bytec extends Opcode { 5 | 6 | // 7 | // The block index parsed from operands. 8 | // 9 | private blockIndex!: number; 10 | 11 | validateOperand(): void { 12 | super.validateOperand(); 13 | 14 | this.blockIndex = Number(this.parseIntOperand(0)); 15 | } 16 | 17 | execute(context: IExecutionContext): void { 18 | this.pushBytes(context, context.bytecblock[this.blockIndex!]); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/lib/opcodes/bytec_X.ts: -------------------------------------------------------------------------------- 1 | import { IToken } from "../token"; 2 | import { Opcode } from "../opcode"; 3 | import { IExecutionContext } from "../context"; 4 | import { IOpcodeDef } from "../opcodes"; 5 | 6 | export class Bytec_X extends Opcode { 7 | 8 | constructor(token: IToken, opcodeDef: IOpcodeDef, private constantIndex: number) { 9 | super(token, opcodeDef); 10 | } 11 | 12 | execute(context: IExecutionContext): void { 13 | this.pushBytes(context, context.bytecblock[this.constantIndex]); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/lib/opcodes/bytecblock.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Bytecblock extends Opcode { 5 | 6 | // 7 | // The byte constant block. 8 | // 9 | private bytecblock: Uint8Array[] = []; 10 | 11 | validateOperand(): void { 12 | super.validateOperand(); 13 | 14 | for (const operand of this.token.operands) { 15 | this.bytecblock.push(new Uint8Array(Buffer.from(operand))); 16 | } 17 | } 18 | 19 | execute(context: IExecutionContext): void { 20 | context.bytecblock = this.bytecblock; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/opcodes/byteor.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { ByteBinary } from "./byte-binary-operator"; 3 | 4 | export class ByteOr extends ByteBinary { 5 | 6 | execute(context: IExecutionContext): void { 7 | const result = this.intA | this.intB; 8 | 9 | // https://developer.algorand.org/docs/reference/teal/specification/#arithmetic-logic-and-cryptographic-operations 10 | const resultBytes = this.intToBytes(result); 11 | const max = Math.max(this.bytesA.length, this.bytesB.length); 12 | const padded = new Uint8Array(Math.max(0, max - resultBytes.length)).fill(0); 13 | const merged = new Uint8Array(max); 14 | merged.set(padded); 15 | merged.set(resultBytes, padded.length); 16 | this.pushBytes(context, merged); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/opcodes/bytexor.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { ByteBinary } from "./byte-binary-operator"; 3 | 4 | export class ByteXor extends ByteBinary { 5 | 6 | execute(context: IExecutionContext): void { 7 | const result = this.intA ^ this.intB; 8 | 9 | // https://developer.algorand.org/docs/reference/teal/specification/#arithmetic-logic-and-cryptographic-operations 10 | const resultBytes = this.intToBytes(result); 11 | const max = Math.max(this.bytesA.length, this.bytesB.length); 12 | const padded = new Uint8Array(Math.max(0, max - resultBytes.length)).fill(0); 13 | const merged = new Uint8Array(max); 14 | merged.set(padded); 15 | merged.set(resultBytes, padded.length); 16 | this.pushBytes(context, merged); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/opcodes/bz.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Bz extends Opcode { 5 | 6 | // 7 | // The name of the target to branch to. 8 | // 9 | private targetName!: string; 10 | 11 | // 12 | // The value to branch on. 13 | // 14 | private condition!: bigint; 15 | 16 | validateOperand() { 17 | super.validateOperand(); 18 | 19 | this.targetName = this.token.operands[0]; 20 | } 21 | 22 | validateContext(context: IExecutionContext) { 23 | const branchTarget = context.branchTargets[this.targetName]; 24 | if (branchTarget === undefined) { 25 | throw new Error(`Failed to find branch target ${this.targetName}.`); 26 | } 27 | 28 | this.condition = this.popInt(context); 29 | } 30 | 31 | execute(context: IExecutionContext): number | void { 32 | if (this.condition === BigInt(0)) { 33 | // Branch if zero. 34 | return context.branchTargets[this.targetName].targetInstructionIndex; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/lib/opcodes/bzero.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class BZero extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | const a = this.popInt(context); 8 | this.pushBytes(context, new Uint8Array(Number(a)).fill(0)); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/opcodes/callsub.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Callsub extends Opcode { 5 | 6 | // 7 | // The name of the target to branch to. 8 | // 9 | private targetName!: string; 10 | 11 | validateOperand() { 12 | super.validateOperand(); 13 | 14 | this.targetName = this.token.operands[0]; 15 | } 16 | 17 | validateContext(context: IExecutionContext) { 18 | const branchTarget = context.branchTargets[this.targetName]; 19 | if (branchTarget === undefined) { 20 | throw new Error(`Failed to find branch target ${this.targetName}.`); 21 | } 22 | } 23 | 24 | execute(context: IExecutionContext): number { 25 | context.callstack.push(context.curInstructionIndex+1); 26 | return context.branchTargets[this.targetName].targetInstructionIndex; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/lib/opcodes/complement.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Complement extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | const value = this.popInt(context); 8 | this.pushInt(context, ~value); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/opcodes/concat.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Concat extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | const b = this.popBytes(context); 8 | const a = this.popBytes(context); 9 | const result = new Uint8Array(a.length + b.length); 10 | result.set(a); 11 | result.set(b, a.length); 12 | this.pushBytes(context, result); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/lib/opcodes/cover.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Cover extends Opcode { 5 | 6 | // 7 | // The number of items above the item covered on the stack. 8 | // 9 | private n!: number; 10 | 11 | validateOperand() { 12 | super.validateOperand(); 13 | 14 | this.n = Number(this.parseIntOperand(0)); 15 | } 16 | 17 | 18 | execute(context: IExecutionContext): void { 19 | const top = context.stack.pop()!; 20 | context.stack.splice(context.stack.length - this.n, 0, top); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/opcodes/dig.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Dig extends Opcode { 5 | 6 | // 7 | // The position on the stack to duplicate. 8 | // 9 | private stackPosition!: number; 10 | 11 | validateOperand() { 12 | super.validateOperand(); 13 | 14 | this.stackPosition = Number(this.parseIntOperand(0)); 15 | } 16 | 17 | validateContext(context: IExecutionContext) { 18 | super.validateContext(context); 19 | 20 | const minStackSize = this.stackPosition + 1; 21 | if (context.stack.length < minStackSize) { 22 | throw new Error(`Found ${context.stack.length} values on the stack. This isn't enough for opcode "${this.token.opcode} ${this.stackPosition}".`); 23 | } 24 | } 25 | 26 | execute(context: IExecutionContext): void { 27 | context.stack.push(context.stack[context.stack.length-1-this.stackPosition]); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/lib/opcodes/div.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Div extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, this.a / this.b); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/divmodw.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Opcode } from "../opcode"; 3 | 4 | export class Divmodw extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | 8 | const d = this.popInt(context); 9 | const c = this.popInt(context); 10 | const b = this.popInt(context); 11 | const a = this.popInt(context); 12 | 13 | const denominator = (c << BigInt('64')) + d; 14 | const numerator = (a << BigInt('64')) + b; 15 | 16 | const result = numerator / denominator; 17 | const maxUint64 = BigInt("0xFFFFFFFFFFFFFFFF"); 18 | const x = result & maxUint64; 19 | const w = result >> BigInt('64'); 20 | 21 | const remainder = numerator % denominator; 22 | const z = remainder & maxUint64; 23 | const y = remainder >> BigInt('64'); 24 | 25 | this.pushInt(context, w); 26 | this.pushInt(context, x); 27 | this.pushInt(context, y); 28 | this.pushInt(context, z); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/lib/opcodes/dup.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Dup extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | context.stack.push(context.stack[context.stack.length-1]); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/dup2.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Dup2 extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | const a = context.stack[context.stack.length-2]; 8 | const b = context.stack[context.stack.length-1]; 9 | context.stack.push(a); 10 | context.stack.push(b); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/opcodes/ecdsa_pk_decompress.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | import { ec as EC } from "elliptic"; 4 | 5 | export class Ecdsa_pk_decompress extends Opcode { 6 | 7 | // 8 | // The curve index parsed from first operand. 9 | /// 10 | private curveIndex!: number; 11 | 12 | validateOperand() { 13 | super.validateOperand(); 14 | 15 | this.curveIndex = Number(this.parseIntOperand(0)); 16 | } 17 | 18 | execute(context: IExecutionContext): void { 19 | 20 | const pubKey = this.popBytes(context); 21 | const ec = new EC('secp256k1'); 22 | const key = ec.keyFromPublic(pubKey, "hex").getPublic(); 23 | this.pushBytes(context, key.getX().toBuffer()); 24 | this.pushBytes(context, key.getY().toBuffer()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/lib/opcodes/ecdsa_pk_recover.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | import { ec as EC } from "elliptic"; 4 | 5 | export class Ecdsa_pk_recover extends Opcode { 6 | 7 | // 8 | // The curve index parsed from first operand. 9 | /// 10 | private curveIndex!: number; 11 | 12 | validateOperand() { 13 | super.validateOperand(); 14 | 15 | this.curveIndex = Number(this.parseIntOperand(0)); 16 | } 17 | 18 | execute(context: IExecutionContext): void { 19 | 20 | const d = this.popBytes(context); 21 | const c = this.popBytes(context); 22 | const b = this.popInt(context); 23 | const a = this.popBytes(context); 24 | 25 | const ec = new EC('secp256k1'); 26 | const pubKey = ec.recoverPubKey(a, { r: c, s: d }, Number(b)); 27 | this.pushBytes(context, pubKey.getX().toBuffer()); 28 | this.pushBytes(context, pubKey.getY().toBuffer()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/lib/opcodes/ecdsa_verify.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | import { ec as EC } from "elliptic"; 4 | 5 | export class Ecdsa_verify extends Opcode { 6 | 7 | // 8 | // The curve index parsed from first operand. 9 | /// 10 | private curveIndex!: number; 11 | 12 | validateOperand() { 13 | super.validateOperand(); 14 | 15 | this.curveIndex = Number(this.parseIntOperand(0)); 16 | } 17 | 18 | execute(context: IExecutionContext): void { 19 | const pubkey2 = context.stack.pop()?.value as Uint8Array; 20 | const pubkey1 = context.stack.pop()?.value as Uint8Array; 21 | const signature2 = context.stack.pop()?.value as Uint8Array; 22 | const signature1 = context.stack.pop()?.value as Uint8Array; 23 | const data = context.stack.pop()?.value as Uint8Array; 24 | 25 | const ec = new EC('secp256k1'); 26 | const key = ec.keyFromPublic({ 27 | x: Buffer.from(pubkey1).toString('hex'), 28 | y: Buffer.from(pubkey2).toString('hex') 29 | }); 30 | if (key.verify(data, { r: signature1, s: signature2 })) { 31 | this.pushInt(context, BigInt(1)); 32 | } 33 | else { 34 | this.pushInt(context, BigInt(0)); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/lib/opcodes/ed25519verify.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | import { encodeAddress, verifyBytes } from "algosdk"; 4 | 5 | export class Ed25519verify extends Opcode { 6 | 7 | execute(context: IExecutionContext): void { 8 | const addr = encodeAddress(this.popBytes(context)); 9 | const signature = this.popBytes(context); 10 | const data = this.popBytes(context); 11 | const isValid = verifyBytes(data, signature, addr); 12 | if (isValid) { 13 | this.pushInt(context, BigInt(1)); 14 | } 15 | else { 16 | this.pushInt(context, BigInt(0)); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/opcodes/eq.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Opcode } from "../opcode"; 3 | 4 | export class Eq extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | const b = context.stack.pop()!; 8 | const a = context.stack.pop()!; 9 | if (a.type !== b.type) { 10 | this.pushInt(context, BigInt(0)); 11 | } 12 | else if (a.type === "bigint") { 13 | this.pushInt(context, a.value === b.value ? BigInt(1) : BigInt(0)); 14 | } 15 | else if (a.type === "byte[]") { 16 | const A = a.value as Uint8Array; 17 | const B = b.value as Uint8Array; 18 | this.pushInt(context, 19 | A.length === B.length && A.every((value, index) => value === B[index]) 20 | ? BigInt(1) 21 | : BigInt(0) 22 | ); 23 | } 24 | else { 25 | throw new Error(`Unexpected value type: ${a.type}`); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/lib/opcodes/err.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Err extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | throw new Error(`Program triggered error using "err" opcode.`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/exp.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Exp extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, this.a ** this.b); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/expw.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Expw extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | const result = this.a ** this.b; 8 | 9 | const maxUint64 = BigInt("0xFFFFFFFFFFFFFFFF"); 10 | const low = result & maxUint64; 11 | const high = result >> BigInt('64'); 12 | 13 | this.pushInt(context, high); 14 | this.pushInt(context, low); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/opcodes/extract.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Extract extends Opcode { 5 | 6 | // 7 | // The start of the range of bytes. 8 | // 9 | private start!: number; 10 | 11 | // 12 | // The length of the range of bytes. 13 | // 14 | private length!: number; 15 | 16 | validateOperand() { 17 | super.validateOperand(); 18 | 19 | this.start = Number(this.parseIntOperand(0)); 20 | this.length = Number(this.parseIntOperand(1)); 21 | } 22 | 23 | 24 | execute(context: IExecutionContext): void { 25 | const bytes = this.popBytes(context); 26 | let length = this.length; 27 | if (length === 0) { 28 | length = bytes.length - this.start; 29 | } 30 | this.pushBytes(context, bytes.slice(this.start, this.start + length)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/lib/opcodes/extract3.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Extract3 extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | const c = Number(this.popInt(context)); 8 | const b = Number(this.popInt(context)); 9 | const a = this.popBytes(context); 10 | this.pushBytes(context, a.slice(b, b + c)); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/opcodes/extractuint_x.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | import { IToken } from "../token"; 4 | import { IOpcodeDef } from "../opcodes"; 5 | 6 | export class ExtractUint_X extends Opcode { 7 | 8 | constructor(token: IToken, opcodeDef: IOpcodeDef, protected numBytes: number) { 9 | super(token, opcodeDef) 10 | } 11 | 12 | execute(context: IExecutionContext): void { 13 | const b = Number(this.popInt(context)); 14 | const a = this.popBytes(context); 15 | this.pushInt(context, this.bytesToInt(a.slice(b, b + this.numBytes))); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/opcodes/gaid.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Gaid extends Opcode { 5 | 6 | // 7 | // The index of the transaction. 8 | // 9 | private txnIndex!: number; 10 | 11 | validateOperand(): void { 12 | super.validateOperand(); 13 | 14 | this.txnIndex = Number(this.parseIntOperand(0)); 15 | if (this.txnIndex < 0) { 16 | throw new Error(`Transaction group index operand of opcode ${this.token.operands} cannot be less than 0.`); 17 | } 18 | } 19 | 20 | async execute(context: IExecutionContext) { 21 | 22 | const value = await context.requireValue(`gaid.${this.txnIndex}`, this.token); 23 | context.stack.push(value); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/lib/opcodes/gaids.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Gaids extends Opcode { 5 | 6 | // 7 | // The index of the transaction. 8 | // 9 | private txnIndex!: number; 10 | 11 | validateContext(context: IExecutionContext): void { 12 | super.validateContext(context); 13 | 14 | this.txnIndex = Number(this.popInt(context)); 15 | if (this.txnIndex < 0) { 16 | throw new Error(`Transaction group index of opcode ${this.token.operands} cannot be less than 0.`); 17 | } 18 | } 19 | 20 | async execute(context: IExecutionContext) { 21 | 22 | const value = await context.requireValue(`gaid.${this.txnIndex}`, this.token); 23 | context.stack.push(value); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/lib/opcodes/getbit.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class GetBit extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | const b = Number(this.popInt(context)); 8 | const a = context.stack.pop()!; 9 | if (a.type === "bigint") { 10 | const binary = a.value.toString(2); 11 | this.pushInt(context, BigInt(binary[binary.length - b - 1])); 12 | } 13 | else if (a.type === "byte[]") { 14 | const byteIndex = Math.floor(b / 8); 15 | const bitIndex = b % 8; 16 | const binary = Array.from(a.value as Uint8Array)[byteIndex].toString(2); 17 | this.pushInt(context, BigInt(binary.padStart(8, "0")[bitIndex])); 18 | } 19 | else { 20 | throw new Error(`Unexpected type ${a.type} found on stack.`); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/lib/opcodes/getbyte.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class GetByte extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | const b = Number(this.popInt(context)); 8 | const a = this.popBytes(context); 9 | this.pushInt(context, BigInt(a[b])); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/lib/opcodes/global.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Global extends Opcode { 5 | 6 | // 7 | // The global to be pushed on the stack. 8 | // 9 | private globalName!: string; 10 | 11 | validateOperand() { 12 | super.validateOperand(); 13 | 14 | this.globalName = this.token.operands[0]; 15 | } 16 | 17 | async execute(context: IExecutionContext): Promise { 18 | 19 | const value = await context.requireValue(`globals.${this.globalName}`, this.token) 20 | context.stack.push(value); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/opcodes/gt.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Gt extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, this.a > this.b ? BigInt(1) : BigInt(0)); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/gte.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Gte extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, this.a >= this.b ? BigInt(1) : BigInt(0)); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/gtxn.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext, ITypedValue } from "../context"; 3 | 4 | export class Gtxn extends Opcode { 5 | 6 | // 7 | // The index of the transction in the current group. 8 | // 9 | private txnIndex!: number; 10 | 11 | // 12 | // The field to be pulled from the transaction. 13 | // 14 | private fieldName!: string; 15 | 16 | validateOperand() { 17 | super.validateOperand(); 18 | 19 | this.txnIndex = Number(this.parseIntOperand(0)); 20 | this.fieldName = this.token.operands[1]; 21 | } 22 | 23 | async execute(context: IExecutionContext): Promise { 24 | 25 | if (this.txnIndex < 0) { 26 | throw new Error(`Transaction index for ${this.opcodeDef} is ${this.txnIndex}, it should be zero or greater.`); 27 | } 28 | 29 | const fieldPath = `gtxn.${this.txnIndex}.${this.fieldName}`; 30 | const value = await context.requireValue(fieldPath, this.token); 31 | context.stack.push(value); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/lib/opcodes/gtxna.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext, ITypedValue } from "../context"; 3 | 4 | export class Gtxna extends Opcode { 5 | 6 | // 7 | // The index of the transaction to get the field of. 8 | // 9 | private txnIndex!: number; 10 | 11 | // 12 | // The field to be pulled from the specified transaction. 13 | // 14 | private fieldName!: string; 15 | 16 | // 17 | // The index in the field. 18 | // 19 | private fieldIndex!: number; 20 | 21 | validateOperand() { 22 | super.validateOperand(); 23 | 24 | this.txnIndex = Number(this.parseIntOperand(0)); 25 | this.fieldName = this.token.operands[1]; 26 | this.fieldIndex = Number(this.parseIntOperand(2)); 27 | } 28 | 29 | async execute(context: IExecutionContext): Promise { 30 | 31 | if (this.txnIndex < 0) { 32 | throw new Error(`Transaction index should >= 0, instead found ${this.txnIndex}`); 33 | } 34 | 35 | if (this.fieldIndex < 0) { 36 | throw new Error(`Field index should be greater than 0, instead got ${this.fieldIndex}.`); 37 | } 38 | 39 | const fieldPath = `gtxn.${this.txnIndex}.${this.fieldName}.${this.fieldIndex}`; 40 | const value = await context.requireValue(fieldPath, this.token); 41 | context.stack.push(value); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/lib/opcodes/gtxns.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Gtxns extends Opcode { 5 | 6 | // 7 | // The index of the specified transaction. 8 | // 9 | private txnIndex!: number; 10 | 11 | // 12 | // The field to be pulled from the specified transaction. 13 | // 14 | private fieldName!: string; 15 | 16 | validateOperand() { 17 | super.validateOperand(); 18 | 19 | this.fieldName = this.token.operands[0]; 20 | } 21 | 22 | validateContext(context: IExecutionContext) { 23 | super.validateContext(context); 24 | 25 | this.txnIndex = Number(this.popInt(context)); 26 | } 27 | 28 | async execute(context: IExecutionContext): Promise { 29 | const fieldPath = `gtxn.${this.txnIndex}.${this.fieldName}`; 30 | const value = await context.requireValue(fieldPath, this.token); 31 | context.stack.push(value); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/lib/opcodes/int.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Int extends Opcode { 5 | 6 | // 7 | // The integer literal value parsed from operands. 8 | // 9 | private value!: bigint; 10 | 11 | validateOperand(): void { 12 | super.validateOperand(); 13 | 14 | this.value = this.parseIntOperand(0); 15 | } 16 | 17 | execute(context: IExecutionContext): void { 18 | this.pushInt(context, this.value); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/lib/opcodes/intc.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Intc extends Opcode { 5 | 6 | // 7 | // The block index parsed from operands. 8 | // 9 | private blockIndex!: number; 10 | 11 | validateOperand(): void { 12 | super.validateOperand(); 13 | 14 | this.blockIndex = Number(this.parseIntOperand(0)); 15 | } 16 | 17 | execute(context: IExecutionContext): void { 18 | this.pushInt(context, context.intcblock[this.blockIndex!]); 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/lib/opcodes/intc_X.ts: -------------------------------------------------------------------------------- 1 | import { IToken } from "../token"; 2 | import { Opcode } from "../opcode"; 3 | import { IExecutionContext } from "../context"; 4 | import { IOpcodeDef } from "../opcodes"; 5 | 6 | export class Intc_X extends Opcode { 7 | 8 | constructor(token: IToken, opcodeDef: IOpcodeDef, private constantIndex: number) { 9 | super(token, opcodeDef); 10 | } 11 | 12 | execute(context: IExecutionContext): void { 13 | this.pushInt(context, context.intcblock[this.constantIndex]); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/lib/opcodes/intcblock.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Intcblock extends Opcode { 5 | 6 | // 7 | // Block of constants to load. 8 | // 9 | private block: bigint[] = []; 10 | 11 | validateOperand(): void { 12 | super.validateOperand(); 13 | 14 | for (let i = 0; i < this.token.operands.length; ++i) { 15 | this.block.push(BigInt(this.parseIntOperand(i))); 16 | } 17 | } 18 | 19 | 20 | execute(context: IExecutionContext): void { 21 | context.intcblock = this.block; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/lib/opcodes/itob.ts: -------------------------------------------------------------------------------- 1 | import { encodeUint64 } from "algosdk"; 2 | import { IExecutionContext } from "../context"; 3 | import { Opcode } from "../opcode"; 4 | 5 | export class Itob extends Opcode { 6 | 7 | // 8 | // The value to be converted. 9 | // 10 | private value!: bigint; 11 | 12 | validateContext(context: IExecutionContext) { 13 | super.validateContext(context); 14 | 15 | this.value = this.popInt(context); 16 | } 17 | 18 | execute(context: IExecutionContext): void { 19 | this.pushBytes(context, encodeUint64(this.value)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/lib/opcodes/itxn.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Itxn extends Opcode { 5 | 6 | // 7 | // The field to be pulled from the current transaction. 8 | // 9 | private fieldName!: string; 10 | 11 | validateOperand() { 12 | super.validateOperand(); 13 | 14 | this.fieldName = this.token.operands[0]; 15 | } 16 | 17 | execute(context: IExecutionContext): void { 18 | 19 | if (context.submittedItxns === undefined || context.submittedItxns.length === 0) { 20 | throw new Error(`No inner transaction has been submited.`); 21 | } 22 | 23 | const value = context.submittedItxns[context.submittedItxns.length-1][this.fieldName]; 24 | if (value === undefined) { 25 | throw new Error(`Field "${this.fieldName}" not found in the last innner transaction..`) 26 | } 27 | 28 | if (Array.isArray(value)) { 29 | throw new Error(`Expected field "${this.fieldName}" not to be an array when used with opcode ${this.token.opcode}.`); 30 | } 31 | 32 | context.stack.push(value); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/lib/opcodes/itxn_begin.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class ItxnBegin extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | context.itxn = {}; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/itxn_field.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class ItxnField extends Opcode { 5 | 6 | // 7 | // The field to be set in the current inner transaction. 8 | // 9 | private fieldName!: string; 10 | 11 | validateOperand() { 12 | super.validateOperand(); 13 | 14 | this.fieldName = this.token.operands[0]; 15 | } 16 | 17 | execute(context: IExecutionContext): void { 18 | 19 | const value = context.stack.pop()!; 20 | 21 | if (context.itxn === undefined) { 22 | throw new Error(`Inner transaction not started with "itxn_begin".`); 23 | 24 | } 25 | 26 | context.itxn[this.fieldName] = value; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/lib/opcodes/itxn_next.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class ItxnNext extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | 8 | if (context.itxn === undefined) { 9 | throw new Error(`Inner transaction not started with "itxn_begin".`); 10 | } 11 | 12 | if (context.submittedItxns === undefined) { 13 | context.submittedItxns = []; 14 | } 15 | 16 | context.submittedItxns.push(context.itxn); 17 | context.itxn = {}; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/opcodes/itxn_submit.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class ItxnSubmit extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | 8 | if (context.itxn === undefined) { 9 | throw new Error(`Inner transaction not started with "itxn_begin".`); 10 | } 11 | 12 | if (context.submittedItxns === undefined) { 13 | context.submittedItxns = []; 14 | } 15 | 16 | context.submittedItxns.push(context.itxn); 17 | context.itxn = undefined; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/opcodes/keccak256.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | const { Keccak } = require("sha3"); 4 | 5 | export class Keccak256 extends Opcode { 6 | 7 | execute(context: IExecutionContext): void { 8 | const value = this.popBytes(context); 9 | const hash = new Keccak(256); 10 | hash.update(Buffer.from(value).toString('utf-8')); 11 | this.pushBytes(context, Uint8Array.from(hash.digest())); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/opcodes/len.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Len extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | const value = context.stack.pop()?.value as Uint8Array; 8 | this.pushInt(context, BigInt(value.length)); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/opcodes/load.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Load extends Opcode { 5 | 6 | // 7 | // The scratch position parsed from operands. 8 | // 9 | private position!: number; 10 | 11 | validateOperand(): void { 12 | super.validateOperand(); 13 | 14 | this.position = Number(this.parseIntOperand(0)); 15 | if (this.position < 0 || this.position >= 255) { 16 | throw new Error(`Invalid position ${this.position} in scratch spaced was requested, this value should be 0 or greater and less than 255.`); 17 | } 18 | } 19 | 20 | execute(context: IExecutionContext): void { 21 | context.stack.push(context.scratch[this.position]); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/lib/opcodes/loads.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Loads extends Opcode { 5 | 6 | // 7 | // The scratch position popped from the stack. 8 | // 9 | private position!: number; 10 | 11 | validateContext(context: IExecutionContext): void { 12 | super.validateContext(context); 13 | 14 | this.position = Number(this.popInt(context)); 15 | if (this.position < 0 || this.position >= 255) { 16 | throw new Error(`Invalid position ${this.position} in scratch spaced was requested, this value should be 0 or greater and less than 255.`); 17 | } 18 | } 19 | 20 | execute(context: IExecutionContext): void { 21 | context.stack.push(context.scratch[this.position]); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/lib/opcodes/log.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Log extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | context.logState.push(this.popBytes(context)); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/lt.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Lt extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, this.a < this.b ? BigInt(1) : BigInt(0)); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/lte.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Lte extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | 8 | this.pushInt(context, this.a <= this.b ? BigInt(1) : BigInt(0)); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/opcodes/min_balance.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext, makeBigInt } from "../context"; 3 | import { decodeAddress } from "../convert"; 4 | 5 | export class MinBalance extends Opcode { 6 | 7 | async execute(context: IExecutionContext) { 8 | const accountName = decodeAddress(this.popBytes(context)); 9 | const value = await context.requireValue(`accounts.${accountName}.minBalance`, this.token); 10 | context.stack.push(makeBigInt(BigInt(value))); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/opcodes/minus.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Minus extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | const result = this.a - this.b; 8 | if (result < BigInt(0)) { 9 | throw new Error(`Subtraction underflow`); 10 | } 11 | this.pushInt(context, result); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/opcodes/mod.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Mod extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, this.a % this.b); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/mul.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Mul extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, this.a * this.b); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/mulw.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Mulw extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | const result = this.a * this.b; 8 | const low = result & BigInt("0xFFFFFFFFFFFFFFFF"); 9 | const high = result >> BigInt('64'); 10 | this.pushInt(context, high); 11 | this.pushInt(context, low); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/opcodes/ne.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Opcode } from "../opcode"; 3 | 4 | export class Ne extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | const b = context.stack.pop()!; 8 | const a = context.stack.pop()!; 9 | if (a.type !== b.type) { 10 | this.pushInt(context, BigInt(1)); 11 | } 12 | else if (a.type === "bigint") { 13 | this.pushInt(context, a.value !== b.value ? BigInt(1) : BigInt(0)); 14 | } 15 | else if (a.type === "byte[]") { 16 | const A = a.value as Uint8Array; 17 | const B = b.value as Uint8Array; 18 | this.pushInt(context, 19 | A.length !== B.length || !A.every((value, index) => value === B[index]) 20 | ? BigInt(1) 21 | : BigInt(0) 22 | ); 23 | } 24 | else { 25 | throw new Error(`Unexpected value type: ${a.type}`); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/lib/opcodes/not.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Not extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | const value = this.popInt(context); 8 | this.pushInt(context, value == BigInt(0) ? BigInt(1) : BigInt(0)); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/opcodes/or.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Or extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, (this.a != BigInt(0) || this.b != BigInt(0)) ? BigInt(1) : BigInt(0)); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/pop.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Pop extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | context.stack.pop(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/pushbytes.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | import { Encoding, stringToBytes } from "../convert"; 4 | 5 | export class PushBytes extends Opcode { 6 | 7 | // 8 | // The type of encoding. 9 | // 10 | private encoding!: string; 11 | 12 | // 13 | // The value to be pushed on the stack. 14 | // 15 | private value!: string; 16 | 17 | validateOperand(): void { 18 | super.validateOperand(); 19 | 20 | this.encoding = "base64"; 21 | this.value = this.token.operands[0]; 22 | if (this.value.length > 1 && this.value.startsWith('"') && this.value.endsWith('"')) { 23 | // Value is a string literal. 24 | this.encoding = "utf8"; 25 | this.value = this.value.slice(1, this.value.length - 2); 26 | } 27 | else if (this.value.startsWith("0x")) { 28 | // Value is hexadecimal. 29 | this.encoding = "hex"; 30 | this.value = this.value.slice(2); 31 | } 32 | else { 33 | throw new Error(`Operand "${this.token.operands[0]}" for opcode "${this.token.operands}" is not a valid format.`); 34 | } 35 | } 36 | 37 | execute(context: IExecutionContext): void { 38 | const bytes = stringToBytes(this.value, this.encoding as Encoding); 39 | this.pushBytes(context, new Uint8Array(bytes)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/lib/opcodes/pushint.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class PushInt extends Opcode { 5 | 6 | // 7 | // The integer literal value parsed from operands. 8 | // 9 | private value!: bigint; 10 | 11 | validateOperand(): void { 12 | super.validateOperand(); 13 | 14 | this.value = this.parseIntOperand(0); 15 | } 16 | 17 | execute(context: IExecutionContext): void { 18 | this.pushInt(context, this.value); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/lib/opcodes/retsub.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Retsub extends Opcode { 5 | 6 | validateContext(context: IExecutionContext) { 7 | if (context.callstack.length < 1) { 8 | throw new Error(`Expected at least one entry on the callstack.`); 9 | } 10 | } 11 | 12 | execute(context: IExecutionContext): number { 13 | return context.callstack.pop()!; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/lib/opcodes/return.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Return extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | context.finished = true; // Request completion. 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/select.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Select extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | const c = this.popInt(context); 8 | const b = context.stack.pop()!; 9 | const a = context.stack.pop()!; 10 | if (c != BigInt(0)) { 11 | context.stack.push(b); 12 | } 13 | else { 14 | context.stack.push(a); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/opcodes/setbyte.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class SetByte extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | const c = Number(this.popInt(context)); 8 | const b = Number(this.popInt(context)); 9 | const a = this.popBytes(context); 10 | a[b] = c; 11 | this.pushBytes(context, a); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/opcodes/sha256.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | import { sha256 } from "js-sha256"; 4 | 5 | export class Sha256 extends Opcode { 6 | 7 | execute(context: IExecutionContext): void { 8 | const value = context.stack.pop()?.value as Uint8Array; 9 | const hash = sha256.create(); 10 | hash.update(value); 11 | this.pushBytes(context, Uint8Array.from(Buffer.from(hash.hex(), 'hex'))); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/opcodes/sha512_256.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | import { sha512_256 } from "js-sha512"; 4 | 5 | export class Sha512_256 extends Opcode { 6 | 7 | execute(context: IExecutionContext): void { 8 | const value = context.stack.pop()?.value as Uint8Array; 9 | const hash = sha512_256.create(); 10 | hash.update(value); 11 | this.pushBytes(context, Uint8Array.from(Buffer.from(hash.hex(), 'hex'))); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/opcodes/shl.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Shl extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, (this.a << this.b) % (BigInt(2) ** BigInt(64))); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/shr.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Shr extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, this.a >> this.b); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/opcodes/sqrt.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Sqrt extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | const value = this.popInt(context); 8 | this.pushInt(context, BigInt(Math.sqrt(Number(value)))); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/opcodes/store.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Store extends Opcode { 5 | 6 | // 7 | // The scratch position parsed from operands. 8 | // 9 | private position!: number; 10 | 11 | validateOperand(): void { 12 | super.validateOperand(); 13 | 14 | this.position = Number(this.parseIntOperand(0)); 15 | if (this.position < 0 || this.position >= 255) { 16 | throw new Error(`Invalid position ${this.position} in scratch spaced was requested, this value should be 0 or greater and less than 255.`); 17 | } 18 | } 19 | 20 | execute(context: IExecutionContext): void { 21 | const value = context.stack.pop()!; 22 | context.scratch[this.position] = value; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/lib/opcodes/stores.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext, ITypedValue } from "../context"; 3 | 4 | export class Stores extends Opcode { 5 | 6 | // 7 | // The value to set in scratch memory. 8 | // 9 | private value!: ITypedValue; 10 | 11 | // 12 | // The scratch position popped from the stack. 13 | // 14 | private position!: number; 15 | 16 | validateContext(context: IExecutionContext): void { 17 | super.validateContext(context); 18 | 19 | this.value = context.stack.pop()!; 20 | 21 | this.position = Number(this.popInt(context)); 22 | if (this.position < 0 || this.position >= 255) { 23 | throw new Error(`Invalid position ${this.position} in scratch spaced was requested, this value should be 0 or greater and less than 255.`); 24 | } 25 | } 26 | 27 | execute(context: IExecutionContext): void { 28 | context.scratch[this.position] = this.value; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/lib/opcodes/substring.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Substring extends Opcode { 5 | 6 | // 7 | // Starting position to extract the substring. 8 | // 9 | private position!: number; 10 | 11 | // 12 | // Ending position to extract the substring. 13 | // 14 | private end!: number; 15 | 16 | validateOperand() { 17 | super.validateOperand(); 18 | 19 | this.position = Number(this.parseIntOperand(0)); 20 | this.end = Number(this.parseIntOperand(1)); 21 | } 22 | 23 | execute(context: IExecutionContext): void { 24 | const value = this.popBytes(context); 25 | this.pushBytes(context, value.subarray(this.position, this.end)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/lib/opcodes/substring3.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Substring3 extends Opcode { 5 | 6 | // 7 | // Value to extract the substring from. 8 | // 9 | private value!: Uint8Array; 10 | 11 | // 12 | // Starting position to extract the substring. 13 | // 14 | private position!: bigint; 15 | 16 | // 17 | // Ending position to extract the substring. 18 | // 19 | private end!: bigint; 20 | 21 | validateContext(context: IExecutionContext) { 22 | super.validateContext(context); 23 | 24 | this.end = this.popInt(context); 25 | this.position = this.popInt(context); 26 | this.value = this.popBytes(context); 27 | } 28 | 29 | execute(context: IExecutionContext): void { 30 | this.pushBytes(context, this.value.subarray(Number(this.position), Number(this.end))); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/lib/opcodes/swap.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Swap extends Opcode { 5 | 6 | execute(context: IExecutionContext): void { 7 | const b = context.stack.pop()!; 8 | const a = context.stack.pop()!; 9 | context.stack.push(b); 10 | context.stack.push(a); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/opcodes/txn.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Txn extends Opcode { 5 | 6 | // 7 | // The field to be pulled from the current transaction. 8 | // 9 | private fieldName!: string; 10 | 11 | validateOperand() { 12 | super.validateOperand(); 13 | 14 | this.fieldName = this.token.operands[0]; 15 | } 16 | 17 | async execute(context: IExecutionContext): Promise { 18 | 19 | const value = await context.requireValue(`txn.${this.fieldName}`, this.token); 20 | context.stack.push(value); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/opcodes/txna.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Txna extends Opcode { 5 | 6 | // 7 | // The index of the transaction to get the field of. 8 | // 9 | private fieldArrayIndex!: number; 10 | 11 | // 12 | // The field to be pulled from the specified transaction. 13 | // 14 | private fieldName!: string; 15 | 16 | validateOperand() { 17 | super.validateOperand(); 18 | 19 | this.fieldName = this.token.operands[0]; 20 | this.fieldArrayIndex = Number(this.parseIntOperand(1)); 21 | } 22 | 23 | async execute(context: IExecutionContext): Promise { 24 | 25 | if (this.fieldArrayIndex < 0) { 26 | throw new Error(`Field index should >= 0, instead found ${this.fieldArrayIndex}`); 27 | } 28 | 29 | const value = await context.requireValue(`txn.${this.fieldName}.${this.fieldArrayIndex}`, this.token) 30 | context.stack.push(value); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/lib/opcodes/txnas.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Txnas extends Opcode { 5 | 6 | // 7 | // The index of the transaction to get the field of. 8 | // 9 | private fieldArrayIndex!: number; 10 | 11 | // 12 | // The field to be pulled from the specified transaction. 13 | // 14 | private fieldName!: string; 15 | 16 | validateOperand() { 17 | super.validateOperand(); 18 | 19 | this.fieldName = this.token.operands[0]; 20 | 21 | } 22 | 23 | validateContext(context: IExecutionContext) { 24 | super.validateContext(context); 25 | 26 | this.fieldArrayIndex = Number(this.popInt(context)); 27 | } 28 | 29 | async execute(context: IExecutionContext): Promise { 30 | 31 | if (this.fieldArrayIndex < 0) { 32 | throw new Error(`Field index should >= 0, instead found ${this.fieldArrayIndex}`); 33 | } 34 | 35 | const value = await context.requireValue(`txn.${this.fieldName}.${this.fieldArrayIndex}`, this.token) 36 | context.stack.push(value); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/lib/opcodes/uncover.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class Uncover extends Opcode { 5 | 6 | // 7 | // The number of items above the item covered on the stack. 8 | // 9 | private n!: number; 10 | 11 | validateOperand() { 12 | super.validateOperand(); 13 | 14 | this.n = Number(this.parseIntOperand(0)); 15 | } 16 | 17 | execute(context: IExecutionContext): void { 18 | const [ item ] = context.stack.splice(context.stack.length - this.n - 1, 1); 19 | context.stack.push(item); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/lib/opcodes/version-pragma.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "../opcode"; 2 | import { IExecutionContext } from "../context"; 3 | 4 | export class VersionPragma extends Opcode { 5 | 6 | // 7 | // The verision number parsed from operands. 8 | // 9 | private versionNo?: number; 10 | 11 | validateOperand(): void { 12 | super.validateOperand(); 13 | 14 | const operand = this.token.operands[1]; 15 | 16 | try { 17 | this.versionNo = parseInt(operand); 18 | } 19 | catch { 20 | throw new Error(`Failed to parse version number from "#pragma version"`); 21 | } 22 | } 23 | 24 | execute(context: IExecutionContext): void { 25 | context.version = this.versionNo!; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/lib/opcodes/xor.ts: -------------------------------------------------------------------------------- 1 | import { IExecutionContext } from "../context"; 2 | import { Binary } from "./binary-operator"; 3 | 4 | export class Xor extends Binary { 5 | 6 | execute(context: IExecutionContext): void { 7 | this.pushInt(context, this.a ^ this.b); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/token.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Represents a parsed instruction. 3 | // 4 | export interface IToken { 5 | 6 | // 7 | // The line number the token was parsed from. 8 | // 9 | lineNo: number; 10 | 11 | // 12 | // The opcode for the instruction. 13 | // 14 | opcode: string; 15 | 16 | // 17 | // Operands for the instruction. 18 | // 19 | operands: string[] 20 | 21 | // 22 | // The comment (if any) that follows the instruction. 23 | // 24 | comment?: string; 25 | } 26 | -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Allows JSON.stringify to serialize a BigInt. 4 | // 5 | export function serializeBigInt(key: string, value: any): any { 6 | return typeof value === 'bigint' 7 | ? value.toString() 8 | : value // return everything else unchanged 9 | }; 10 | 11 | // 12 | // Stringifies an object for display. 13 | // 14 | export function stringify(obj: any): string { 15 | return JSON.stringify(obj, serializeBigInt, 4); 16 | } 17 | 18 | 19 | 20 | // 21 | // Dumps an object using console.log. 22 | // 23 | export function dump(obj: any): void { 24 | console.log(stringify(obj)); 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/test/auto-create.test.ts: -------------------------------------------------------------------------------- 1 | import { autoCreateField } from "../lib/auto-field"; 2 | import { makeBigInt } from "../lib/context"; 3 | 4 | describe("auto create", () => { 5 | 6 | it("can auto create root field", () => { 7 | 8 | const container: any = {}; 9 | autoCreateField(container, "a", makeBigInt(BigInt(5))); 10 | expect(Number(container.a.value)).toEqual(5); 11 | }); 12 | 13 | it("can auto create nested field", () => { 14 | 15 | const container: any = {}; 16 | autoCreateField(container, "a.b.c", makeBigInt(BigInt(5))); 17 | expect(Number(container.a.b.c.value)).toEqual(5); 18 | }); 19 | }); -------------------------------------------------------------------------------- /src/test/opcodes/Ecdsa_pk_decompress.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { encodeAddress, stringToBytes } from "../../lib/convert"; 3 | import { opcodeDefs } from "../../lib/opcodes"; 4 | import { Ecdsa_pk_decompress } from "../../lib/opcodes/ecdsa_pk_decompress"; 5 | import { ec as EC } from "elliptic"; 6 | 7 | describe("ecdsa_pk_decompress opcode", () => { 8 | 9 | it ("can execute", () => { 10 | 11 | const token: any = { 12 | operands: [ 13 | "0", 14 | ], 15 | }; 16 | const opcode = new Ecdsa_pk_decompress(token, opcodeDefs.ecdsa_pk_decompress); 17 | 18 | const ec = new EC('secp256k1'); 19 | 20 | const context: any = { 21 | stack: [ 22 | makeBytes(Buffer.from("0250863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B2352", "hex")), 23 | ], 24 | }; 25 | 26 | opcode.validateOperand(); 27 | opcode.execute(context); 28 | 29 | expect(context.stack.length).toEqual(2); 30 | expect(context.stack[0].value).toEqual(Buffer.from('50863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B2352', "hex")); 31 | expect(context.stack[1].value).toEqual(Buffer.from('2CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6', "hex")); 32 | }); 33 | }); -------------------------------------------------------------------------------- /src/test/opcodes/Ecdsa_pk_recover.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt, makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Ecdsa_pk_recover } from "../../lib/opcodes/ecdsa_pk_recover"; 4 | import { ec as EC } from "elliptic"; 5 | 6 | describe("ecdsa_pk_recover opcode", () => { 7 | 8 | it ("can execute", () => { 9 | 10 | const token: any = { 11 | operands: [ 12 | "0", 13 | ], 14 | }; 15 | const opcode = new Ecdsa_pk_recover(token, opcodeDefs.ecdsa_pk_recover); 16 | 17 | const hash = new Uint8Array([0, 1, 2, 3, 4, 5]); 18 | const ec = new EC('secp256k1'); 19 | const key = ec.genKeyPair(); 20 | const sig = key.sign(hash); 21 | 22 | const context: any = { 23 | stack: [ 24 | makeBytes(hash), 25 | makeBigInt(BigInt(sig.recoveryParam || 0)), 26 | makeBytes(sig.r.toBuffer()), 27 | makeBytes(sig.s.toBuffer()), 28 | ], 29 | }; 30 | 31 | opcode.validateOperand(); 32 | opcode.execute(context); 33 | 34 | expect(context.stack.length).toEqual(2); 35 | expect(context.stack[0].value).toEqual(key.getPublic().getX().toBuffer()); 36 | expect(context.stack[1].value).toEqual(key.getPublic().getY().toBuffer()); 37 | }); 38 | }); -------------------------------------------------------------------------------- /src/test/opcodes/add.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Add } from "../../lib/opcodes/add"; 4 | 5 | describe("add opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(3)), 13 | makeBigInt(BigInt(4)), 14 | ], 15 | }; 16 | const opcode = new Add(token, opcodeDefs["+"]); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(context.stack[0].type === "bigint"); 22 | expect(Number(context.stack[0].value)).toEqual(7); 23 | }); 24 | }); -------------------------------------------------------------------------------- /src/test/opcodes/addr.test.ts: -------------------------------------------------------------------------------- 1 | import { decodeAddress } from "../../lib/convert"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Addr } from "../../lib/opcodes/addr"; 4 | 5 | describe("addr opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const addr = "7JOPVEP3ABJUW5YZ5WFIONLPWTZ5MYX5HFK4K7JLGSIAG7RRB42MNLQ224"; 10 | const token: any = { 11 | operands: [ 12 | addr, 13 | ], 14 | }; 15 | const opcode = new Addr(token, opcodeDefs.addr); 16 | opcode.validateOperand(); // Parses the operand. 17 | 18 | const context: any = { 19 | stack: [], 20 | }; 21 | opcode.execute(context); 22 | 23 | expect(context.stack.length).toEqual(1); 24 | expect(decodeAddress(context.stack[0]?.value)).toEqual(addr); 25 | }); 26 | 27 | }); -------------------------------------------------------------------------------- /src/test/opcodes/addw.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Addw } from "../../lib/opcodes/addw"; 4 | 5 | describe("addw opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(3)), 13 | makeBigInt(BigInt(2)), 14 | ], 15 | }; 16 | const opcode = new Addw(token, opcodeDefs.addw); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(2); 21 | expect(Number(context.stack[0]?.value)).toEqual(0); 22 | expect(Number(context.stack[1]?.value)).toEqual(5); 23 | }); 24 | }); -------------------------------------------------------------------------------- /src/test/opcodes/and.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { And } from "../../lib/opcodes/and"; 4 | 5 | describe("and opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(3)), 13 | makeBigInt(BigInt(2)), 14 | ], 15 | }; 16 | const opcode = new And(token, opcodeDefs["&&"]); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Number(context.stack[0]?.value)).toEqual(1); 22 | }); 23 | }); -------------------------------------------------------------------------------- /src/test/opcodes/app_global_del.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt, makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { AppGlobalDel } from "../../lib/opcodes/app_global_del"; 4 | 5 | describe("app_global_del opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new AppGlobalDel(token, opcodeDefs.app_global_del); 11 | 12 | const context: any = { 13 | appGlobals: { 14 | "0": { 15 | aGlobal: makeBigInt(BigInt(2)), 16 | }, 17 | }, 18 | stack: [ 19 | makeBytes(new Uint8Array(Buffer.from("aGlobal"))), 20 | ], 21 | }; 22 | opcode.execute(context); 23 | 24 | expect(context.stack.length).toEqual(0); 25 | expect(context.appGlobals["0"].aGlobal).not.toBeDefined(); 26 | }); 27 | 28 | }); -------------------------------------------------------------------------------- /src/test/opcodes/app_global_get.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt, makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { AppGlobalGet } from "../../lib/opcodes/app_global_get"; 4 | 5 | describe("app_global_get opcode", () => { 6 | 7 | it ("can execute", async () => { 8 | 9 | const globalName = "aGlobal"; 10 | const token: any = {}; 11 | const opcode = new AppGlobalGet(token, opcodeDefs.app_global_get); 12 | 13 | const context: any = { 14 | requireValue: (fieldPath: string) => { 15 | expect(fieldPath).toEqual(`appGlobals.0.${globalName}`); 16 | 17 | return makeBigInt(BigInt(4)); 18 | }, 19 | stack: [ 20 | makeBytes(new Uint8Array(Buffer.from(globalName))), 21 | ], 22 | }; 23 | await opcode.execute(context); 24 | 25 | expect(context.stack.length).toEqual(1); 26 | expect(Number(context.stack[0]?.value)).toEqual(4); 27 | }); 28 | 29 | }); -------------------------------------------------------------------------------- /src/test/opcodes/app_local_del.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt, makeBytes } from "../../lib/context"; 2 | import { encodeAddress } from "../../lib/convert"; 3 | import { opcodeDefs } from "../../lib/opcodes"; 4 | import { AppLocalDel } from "../../lib/opcodes/app_local_del"; 5 | 6 | describe("app_local_del opcode", () => { 7 | 8 | it ("can execute", async () => { 9 | 10 | const addr = "john"; 11 | const account = { 12 | appLocals: { 13 | "0": { 14 | aLocal: makeBigInt(BigInt(2)), 15 | }, 16 | }, 17 | }; 18 | const token: any = {}; 19 | const opcode = new AppLocalDel(token, opcodeDefs.app_local_del); 20 | 21 | const context: any = { 22 | requireValue: async (fieldPath: string) => { 23 | expect(fieldPath).toEqual(`accounts.${addr}`); 24 | return account; 25 | }, 26 | stack: [ 27 | makeBytes(encodeAddress(addr)), 28 | makeBytes(new Uint8Array(Buffer.from("aLocal"))), 29 | ], 30 | }; 31 | await opcode.execute(context); 32 | 33 | expect(context.stack.length).toEqual(0); 34 | expect(account.appLocals["0"].aLocal).not.toBeDefined(); 35 | }); 36 | 37 | }); -------------------------------------------------------------------------------- /src/test/opcodes/app_local_get.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt, makeBytes } from "../../lib/context"; 2 | import { encodeAddress } from "../../lib/convert"; 3 | import { opcodeDefs } from "../../lib/opcodes"; 4 | import { AppLocalGet } from "../../lib/opcodes/app_local_get"; 5 | 6 | describe("app_local_get opcode", () => { 7 | 8 | it ("can execute", async () => { 9 | 10 | const addr = "7JOPVEP3ABJUW5YZ5WFIONLPWTZ5MYX5HFK4K7JLGSIAG7RRB42MNLQ224"; 11 | const localName = "aLocal"; 12 | const account = {}; 13 | const token: any = {}; 14 | const opcode = new AppLocalGet(token, opcodeDefs.app_local_get); 15 | 16 | const context: any = { 17 | requireAccount: async (accountName: string) => { 18 | expect(accountName).toEqual(addr); 19 | return account; 20 | }, 21 | requestValue: async (fieldPath: string) => { 22 | expect(fieldPath).toEqual(`accounts.${addr}.appLocals.0.${localName}`); 23 | 24 | return makeBigInt(BigInt(3)); 25 | }, 26 | stack: [ 27 | makeBytes(encodeAddress(addr)), 28 | makeBytes(new Uint8Array(Buffer.from(localName))), 29 | ], 30 | }; 31 | await opcode.execute(context); 32 | 33 | expect(context.stack.length).toEqual(1); 34 | expect(Number(context.stack[0]?.value)).toEqual(3); 35 | }); 36 | }); -------------------------------------------------------------------------------- /src/test/opcodes/app_local_get_ex.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt, makeBytes } from "../../lib/context"; 2 | import { encodeAddress } from "../../lib/convert"; 3 | import { opcodeDefs } from "../../lib/opcodes"; 4 | import { AppLocalGetEx } from "../../lib/opcodes/app_local_get_ex"; 5 | 6 | describe("app_local_get_ex opcode", () => { 7 | 8 | it ("can execute", async () => { 9 | 10 | const addr = "john"; 11 | const appId = "2"; 12 | const localName = "aLocal"; 13 | const token: any = {}; 14 | const opcode = new AppLocalGetEx(token, opcodeDefs.app_local_get_ex); 15 | 16 | const context: any = { 17 | requestValue: async (fieldPath: string) => { 18 | expect(fieldPath).toEqual(`accounts.${addr}.appLocals.${appId}.${localName}`); 19 | return makeBigInt(BigInt(3)); 20 | }, 21 | stack: [ 22 | makeBytes(encodeAddress(addr)), 23 | makeBigInt(BigInt(2)), 24 | makeBytes(new Uint8Array(Buffer.from(localName))), 25 | ], 26 | }; 27 | await opcode.execute(context); 28 | 29 | expect(context.stack.length).toEqual(2); 30 | expect(Number(context.stack[0]?.value)).toEqual(3); 31 | expect(Number(context.stack[1]?.value)).toEqual(1); 32 | }); 33 | 34 | }); -------------------------------------------------------------------------------- /src/test/opcodes/arg.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Arg } from "../../lib/opcodes/arg"; 4 | 5 | describe("arg opcode", () => { 6 | 7 | it("can push arg on stack", async () => { 8 | 9 | const argIndex = 0; 10 | const token: any = { 11 | operands: [ 12 | argIndex.toString(), 13 | ], 14 | }; 15 | const opcode = new Arg(token, opcodeDefs.arg); 16 | 17 | const context: any = { 18 | requireValue: (fieldPath: string) => { 19 | expect(fieldPath).toEqual(`args.${argIndex}`); 20 | return makeBigInt(BigInt(12)); 21 | }, 22 | stack : [], 23 | }; 24 | 25 | opcode.validateOperand(); 26 | await opcode.execute(context); 27 | 28 | expect(context.stack.length).toEqual(1); 29 | expect(Number(context.stack[0]?.value)).toEqual(12); 30 | }); 31 | 32 | it("throws when operand is not an int", () => { 33 | 34 | const token: any = { 35 | operands: [ 36 | "xxx" 37 | ], 38 | }; 39 | const opcode = new Arg(token, opcodeDefs.arg); 40 | expect(() => opcode.validateOperand()).toThrow(); 41 | }); 42 | 43 | }); -------------------------------------------------------------------------------- /src/test/opcodes/arg_X.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Arg_X } from "../../lib/opcodes/arg_X"; 4 | 5 | describe("arg_X opcode", () => { 6 | 7 | it("can push arg on stack", async () => { 8 | 9 | const argIndex = 0; 10 | const token: any = {}; 11 | const opcode = new Arg_X(token, opcodeDefs.arg_X, argIndex); 12 | 13 | const context: any = { 14 | requireValue: (fieldPath: string) => { 15 | expect(fieldPath).toEqual(`args.${argIndex}`); 16 | return makeBigInt(BigInt(12)); 17 | }, 18 | stack : [], 19 | }; 20 | 21 | await opcode.execute(context); 22 | 23 | expect(context.stack.length).toEqual(1); 24 | expect(Number(context.stack[0]?.value)).toEqual(12); 25 | }); 26 | 27 | }); -------------------------------------------------------------------------------- /src/test/opcodes/args.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Args } from "../../lib/opcodes/args"; 4 | 5 | describe("args opcode", () => { 6 | 7 | it("can push arg on stack", async () => { 8 | 9 | const argIndex = 0; 10 | const token: any = {}; 11 | const opcode = new Args(token, opcodeDefs.args); 12 | 13 | const context: any = { 14 | requireValue: (fieldPath: string) => { 15 | expect(fieldPath).toEqual(`args.${argIndex}`); 16 | return makeBigInt(BigInt(12)); 17 | }, 18 | stack : [ 19 | makeBigInt(BigInt(argIndex)), 20 | ], 21 | }; 22 | 23 | opcode.validateContext(context); 24 | await opcode.execute(context); 25 | 26 | expect(context.stack.length).toEqual(1); 27 | expect(Number(context.stack[0]?.value)).toEqual(12); 28 | }); 29 | 30 | }); -------------------------------------------------------------------------------- /src/test/opcodes/balance.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { encodeAddress } from "../../lib/convert"; 3 | import { opcodeDefs } from "../../lib/opcodes"; 4 | import { Balance } from "../../lib/opcodes/balance"; 5 | 6 | describe("balance opcode", () => { 7 | 8 | it ("can execute", async () => { 9 | 10 | const addr = "john"; 11 | const token: any = {}; 12 | const opcode = new Balance(token, opcodeDefs.balance); 13 | 14 | const context: any = { 15 | requireValue: async (fieldPath: string) => { 16 | expect(fieldPath).toEqual(`accounts.${addr}.balance`); 17 | return 12; 18 | }, 19 | stack: [ 20 | makeBytes(encodeAddress(addr)), 21 | ], 22 | }; 23 | await opcode.execute(context); 24 | 25 | expect(context.stack.length).toEqual(1); 26 | expect(Number(context.stack[0]?.value)).toEqual(12); 27 | }); 28 | 29 | }); -------------------------------------------------------------------------------- /src/test/opcodes/band.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Band } from "../../lib/opcodes/band"; 4 | 5 | describe("bitwise and opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(2)), 13 | makeBigInt(BigInt(6)), 14 | ], 15 | }; 16 | const opcode = new Band(token, opcodeDefs["&"]); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Number(context.stack[0]?.value)).toEqual(2); 22 | }); 23 | }); -------------------------------------------------------------------------------- /src/test/opcodes/bdiv.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { BDiv } from "../../lib/opcodes/bdiv"; 4 | 5 | describe("bdiv opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new BDiv(token, opcodeDefs["b/"]); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(Buffer.from("06", "hex")), 15 | makeBytes(Buffer.from("03", "hex")), 16 | ], 17 | }; 18 | opcode.validateContext(context); 19 | opcode.execute(context); 20 | 21 | expect(context.stack.length).toEqual(1); 22 | expect(Array.from(context.stack[0].value)).toEqual([ 2 ]); 23 | }); 24 | 25 | }); -------------------------------------------------------------------------------- /src/test/opcodes/beq.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Beq } from "../../lib/opcodes/beq"; 4 | 5 | describe("b== opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new Beq(token, opcodeDefs["b=="]); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(Buffer.from("A")), 15 | makeBytes(Buffer.from("B")), 16 | ], 17 | }; 18 | opcode.validateContext(context); 19 | opcode.execute(context); 20 | 21 | expect(context.stack.length).toEqual(1); 22 | expect(Number(context.stack[0].value)).toEqual(0); 23 | }); 24 | 25 | }); -------------------------------------------------------------------------------- /src/test/opcodes/bgt.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Bgt } from "../../lib/opcodes/bgt"; 4 | 5 | describe("b> opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new Bgt(token, opcodeDefs["b>"]); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(Buffer.from("A")), 15 | makeBytes(Buffer.from("B")), 16 | ], 17 | }; 18 | opcode.validateContext(context); 19 | opcode.execute(context); 20 | 21 | expect(context.stack.length).toEqual(1); 22 | expect(Number(context.stack[0].value)).toEqual(0); 23 | }); 24 | 25 | }); -------------------------------------------------------------------------------- /src/test/opcodes/bgte.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Bgte } from "../../lib/opcodes/bgte"; 4 | 5 | describe("b>= opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new Bgte(token, opcodeDefs["b>="]); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(Buffer.from("A")), 15 | makeBytes(Buffer.from("B")), 16 | ], 17 | }; 18 | opcode.validateContext(context); 19 | opcode.execute(context); 20 | 21 | expect(context.stack.length).toEqual(1); 22 | expect(Number(context.stack[0].value)).toEqual(0); 23 | }); 24 | 25 | }); -------------------------------------------------------------------------------- /src/test/opcodes/binvert.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { BInvert } from "../../lib/opcodes/binvert"; 4 | 5 | describe("binvert opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new BInvert(token, opcodeDefs.binvert); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(Buffer.from("01", "hex")), 15 | ], 16 | }; 17 | opcode.execute(context); 18 | 19 | expect(context.stack.length).toEqual(1); 20 | expect(context.stack[0].value).toEqual(Buffer.from("fe", "hex")); 21 | }); 22 | 23 | }); -------------------------------------------------------------------------------- /src/test/opcodes/bitlen.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt, makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Bitlen } from "../../lib/opcodes/bitlen"; 4 | 5 | describe("bitlen opcode", () => { 6 | 7 | it ("can execute against big int", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(5)), 13 | ], 14 | }; 15 | 16 | const opcode = new Bitlen(token, opcodeDefs.bitlen); 17 | opcode.execute(context); 18 | 19 | expect(context.stack.length).toEqual(1); 20 | expect(Number(context.stack[0]!.value)).toEqual(3); 21 | }); 22 | 23 | it ("can execute against byte array", () => { 24 | 25 | const token: any = {}; 26 | const context: any = { 27 | stack: [ 28 | makeBytes(new Uint8Array([1, 2, 3, 4])), 29 | ], 30 | }; 31 | 32 | const opcode = new Bitlen(token, opcodeDefs.bitlen); 33 | opcode.execute(context); 34 | 35 | expect(context.stack.length).toEqual(1); 36 | expect(Number(context.stack[0]!.value)).toEqual(25); 37 | }); 38 | 39 | }); -------------------------------------------------------------------------------- /src/test/opcodes/blt.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Blt } from "../../lib/opcodes/blt"; 4 | 5 | describe("b< opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new Blt(token, opcodeDefs["b<"]); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(Buffer.from("A")), 15 | makeBytes(Buffer.from("B")), 16 | ], 17 | }; 18 | opcode.validateContext(context); 19 | opcode.execute(context); 20 | 21 | expect(context.stack.length).toEqual(1); 22 | expect(Number(context.stack[0].value)).toEqual(1); 23 | }); 24 | 25 | }); -------------------------------------------------------------------------------- /src/test/opcodes/blte.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Blte } from "../../lib/opcodes/blte"; 4 | 5 | describe("b<= opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new Blte(token, opcodeDefs["b<="]); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(Buffer.from("A")), 15 | makeBytes(Buffer.from("B")), 16 | ], 17 | }; 18 | opcode.validateContext(context); 19 | opcode.execute(context); 20 | 21 | expect(context.stack.length).toEqual(1); 22 | expect(Number(context.stack[0].value)).toEqual(1); 23 | }); 24 | 25 | }); -------------------------------------------------------------------------------- /src/test/opcodes/bminus.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { BMinus } from "../../lib/opcodes/bminus"; 4 | 5 | describe("bminus opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new BMinus(token, opcodeDefs["b-"]); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(Buffer.from("03", "hex")), 15 | makeBytes(Buffer.from("02", "hex")), 16 | ], 17 | }; 18 | opcode.validateContext(context); 19 | opcode.execute(context); 20 | 21 | expect(context.stack.length).toEqual(1); 22 | expect(Array.from(context.stack[0].value)).toEqual([ 1 ]); 23 | }); 24 | 25 | }); -------------------------------------------------------------------------------- /src/test/opcodes/bmod.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { BMod } from "../../lib/opcodes/bmod"; 4 | 5 | describe("bmod opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new BMod(token, opcodeDefs["b%"]); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(Buffer.from("09", "hex")), 15 | makeBytes(Buffer.from("02", "hex")), 16 | ], 17 | }; 18 | opcode.validateContext(context); 19 | opcode.execute(context); 20 | 21 | expect(context.stack.length).toEqual(1); 22 | expect(Array.from(context.stack[0].value)).toEqual([ 1 ]); 23 | }); 24 | 25 | }); -------------------------------------------------------------------------------- /src/test/opcodes/bmul.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { BMul } from "../../lib/opcodes/bmul"; 4 | 5 | describe("bmul opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new BMul(token, opcodeDefs["b*"]); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(Buffer.from("02", "hex")), 15 | makeBytes(Buffer.from("03", "hex")), 16 | ], 17 | }; 18 | opcode.validateContext(context); 19 | opcode.execute(context); 20 | 21 | expect(context.stack.length).toEqual(1); 22 | expect(Array.from(context.stack[0].value)).toEqual([ 6 ]); 23 | }); 24 | 25 | }); -------------------------------------------------------------------------------- /src/test/opcodes/bne.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Bne } from "../../lib/opcodes/bne"; 4 | 5 | describe("b!= opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new Bne(token, opcodeDefs["b!="]); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(Buffer.from("A")), 15 | makeBytes(Buffer.from("B")), 16 | ], 17 | }; 18 | opcode.validateContext(context); 19 | opcode.execute(context); 20 | 21 | expect(context.stack.length).toEqual(1); 22 | expect(Number(context.stack[0].value)).toEqual(1); 23 | }); 24 | 25 | }); -------------------------------------------------------------------------------- /src/test/opcodes/bor.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Bor } from "../../lib/opcodes/bor"; 4 | 5 | describe("bitwise or opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(1)), 13 | makeBigInt(BigInt(3)), 14 | ], 15 | }; 16 | const opcode = new Bor(token, opcodeDefs["|"]); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Number(context.stack[0]?.value)).toEqual(3); 22 | }); 23 | }); -------------------------------------------------------------------------------- /src/test/opcodes/bplus.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { BPlus } from "../../lib/opcodes/bplus"; 4 | 5 | describe("bplus opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new BPlus(token, opcodeDefs["b+"]); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(Buffer.from("01", "hex")), 15 | makeBytes(Buffer.from("02", "hex")), 16 | ], 17 | }; 18 | opcode.validateContext(context); 19 | opcode.execute(context); 20 | 21 | expect(context.stack.length).toEqual(1); 22 | expect(Array.from(context.stack[0].value)).toEqual([ 3 ]); 23 | }); 24 | 25 | }); -------------------------------------------------------------------------------- /src/test/opcodes/branch.test.ts: -------------------------------------------------------------------------------- 1 | import { opcodeDefs } from "../../lib/opcodes"; 2 | import { Branch } from "../../lib/opcodes/branch"; 3 | 4 | describe("branch opcode", () => { 5 | 6 | it("throws when branch target doesn't exist", () => { 7 | 8 | const token: any = { 9 | operands: [ 10 | "a-label", 11 | ], 12 | }; 13 | const opcode = new Branch(token, opcodeDefs.b) 14 | 15 | const context: any = { 16 | branchTargets: { 17 | // No branch target. 18 | }, 19 | }; 20 | opcode.validateOperand(); 21 | expect(() => opcode.validateContext(context)).toThrow(); 22 | }); 23 | 24 | it ("can execute", () => { 25 | 26 | const token: any = { 27 | operands: [ 28 | "a-label", 29 | ], 30 | }; 31 | const opcode = new Branch(token, opcodeDefs.b); 32 | 33 | const context: any = { 34 | branchTargets: { 35 | "a-label": { 36 | targetInstructionIndex: 32, 37 | }, 38 | }, 39 | }; 40 | opcode.validateOperand(); 41 | expect(opcode.execute(context)).toEqual(32); 42 | }); 43 | }); -------------------------------------------------------------------------------- /src/test/opcodes/byteand.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { ByteAnd } from "../../lib/opcodes/byteand"; 4 | 5 | describe("b& opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new ByteAnd(token, opcodeDefs["b&"]); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(Buffer.from("11", "hex")), 15 | makeBytes(Buffer.from("10", "hex")), 16 | ], 17 | }; 18 | opcode.validateContext(context); 19 | opcode.execute(context); 20 | 21 | expect(context.stack.length).toEqual(1); 22 | expect(Array.from(context.stack[0].value)).toEqual([ 16 ]); 23 | }); 24 | 25 | }); -------------------------------------------------------------------------------- /src/test/opcodes/bytec.test.ts: -------------------------------------------------------------------------------- 1 | import { opcodeDefs } from "../../lib/opcodes"; 2 | import { Bytec } from "../../lib/opcodes/bytec"; 3 | 4 | describe("bytec opcode", () => { 5 | 6 | it ("can execute", () => { 7 | 8 | const token: any = { 9 | operands: [ 10 | "2" 11 | ], 12 | }; 13 | const opcode = new Bytec(token, opcodeDefs.bytec); 14 | opcode.validateOperand(); // Parses the operand. 15 | 16 | const context: any = { 17 | stack: [], 18 | bytecblock: [ 19 | new Uint8Array([1, 2]), 20 | new Uint8Array([3, 4]), 21 | new Uint8Array([5, 6]), 22 | ], 23 | }; 24 | opcode.execute(context); 25 | 26 | expect(context.stack.length).toEqual(1); 27 | expect(Array.from(context.stack[0]?.value)).toEqual([5, 6]); 28 | }); 29 | 30 | it("throws when operand is not an int", () => { 31 | 32 | const token: any = { 33 | operands: [ 34 | "xxx" 35 | ], 36 | }; 37 | const opcode = new Bytec(token, opcodeDefs.bytec); 38 | 39 | expect(() => opcode.validateOperand()).toThrow(); 40 | }); 41 | 42 | }); -------------------------------------------------------------------------------- /src/test/opcodes/bytec_X.test.ts: -------------------------------------------------------------------------------- 1 | import { opcodeDefs } from "../../lib/opcodes"; 2 | import { Bytec_X } from "../../lib/opcodes/bytec_X"; 3 | 4 | describe("bytec_X opcode", () => { 5 | 6 | it ("can execute with index 0", () => { 7 | 8 | const token: any = {}; 9 | const opcode = new Bytec_X(token, opcodeDefs.bytec_0, 0); 10 | 11 | const context: any = { 12 | stack: [], 13 | bytecblock: [ 14 | new Uint8Array([1, 2]), 15 | ], 16 | }; 17 | opcode.execute(context); 18 | 19 | expect(context.stack.length).toEqual(1); 20 | expect(Array.from(context.stack[0]?.value)).toEqual([1, 2]); 21 | }); 22 | 23 | it ("can execute with index 2", () => { 24 | 25 | const token: any = {}; 26 | const opcode = new Bytec_X(token, opcodeDefs.bytec_2, 2); 27 | 28 | const context: any = { 29 | stack: [], 30 | bytecblock: [ 31 | new Uint8Array([1, 2]), 32 | new Uint8Array([3, 4]), 33 | new Uint8Array([5, 6]), 34 | ], 35 | }; 36 | opcode.execute(context); 37 | 38 | expect(context.stack.length).toEqual(1); 39 | expect(Array.from(context.stack[0]?.value)).toEqual([5, 6]); 40 | }); 41 | 42 | }); -------------------------------------------------------------------------------- /src/test/opcodes/bytecblock.test.ts: -------------------------------------------------------------------------------- 1 | import { opcodeDefs } from "../../lib/opcodes"; 2 | import { Bytecblock } from "../../lib/opcodes/bytecblock"; 3 | 4 | describe("bytecblock opcode", () => { 5 | 6 | it ("can execute", () => { 7 | 8 | const token: any = { 9 | operands: [ 10 | "1", 11 | "2", 12 | "3", 13 | ], 14 | }; 15 | 16 | const opcode = new Bytecblock(token, opcodeDefs.bytecblock); 17 | opcode.validateOperand(); // Parses the operand. 18 | 19 | const context: any = { 20 | stack: [], 21 | }; 22 | opcode.execute(context); 23 | 24 | expect(context.bytecblock.length).toEqual(3); 25 | expect(Array.from(context.bytecblock[0])).toEqual([ 49 ]); 26 | expect(Array.from(context.bytecblock[1])).toEqual([ 50 ]); 27 | expect(Array.from(context.bytecblock[2])).toEqual([ 51 ]); 28 | }); 29 | 30 | }); -------------------------------------------------------------------------------- /src/test/opcodes/byteor.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { ByteOr } from "../../lib/opcodes/byteor"; 4 | 5 | describe("b| opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new ByteOr(token, opcodeDefs["b|"]); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(Buffer.from("01", "hex")), 15 | makeBytes(Buffer.from("10", "hex")), 16 | ], 17 | }; 18 | opcode.validateContext(context); 19 | opcode.execute(context); 20 | 21 | expect(context.stack.length).toEqual(1); 22 | expect(Array.from(context.stack[0].value)).toEqual([ 17 ]); 23 | }); 24 | 25 | }); -------------------------------------------------------------------------------- /src/test/opcodes/bytexor.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { ByteXor } from "../../lib/opcodes/bytexor"; 4 | 5 | describe("b^ opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new ByteXor(token, opcodeDefs["b^"]); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(Buffer.from("11", "hex")), 15 | makeBytes(Buffer.from("10", "hex")), 16 | ], 17 | }; 18 | opcode.validateContext(context); 19 | opcode.execute(context); 20 | 21 | expect(context.stack.length).toEqual(1); 22 | expect(Array.from(context.stack[0].value)).toEqual([ 1 ]); 23 | }); 24 | 25 | }); -------------------------------------------------------------------------------- /src/test/opcodes/bzero.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { BZero } from "../../lib/opcodes/bzero"; 4 | 5 | describe("bzero opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new BZero(token, opcodeDefs.bzero); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBigInt(BigInt(3)) 15 | ], 16 | }; 17 | opcode.execute(context); 18 | 19 | expect(context.stack.length).toEqual(1); 20 | expect(Array.from(context.stack[0].value)).toEqual([0, 0, 0]); 21 | }); 22 | 23 | }); -------------------------------------------------------------------------------- /src/test/opcodes/complement.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Complement } from "../../lib/opcodes/complement"; 4 | 5 | describe("complement opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(3)), 13 | ], 14 | }; 15 | const opcode = new Complement(token, opcodeDefs["~"]); 16 | opcode.execute(context); 17 | 18 | expect(context.stack.length).toEqual(1); 19 | expect(Number(context.stack[0]?.value)).toEqual(-4); 20 | }); 21 | }); -------------------------------------------------------------------------------- /src/test/opcodes/concat.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt, makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Concat } from "../../lib/opcodes/concat"; 4 | 5 | describe("concat opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBytes(new Uint8Array([1, 2])), 13 | makeBytes(new Uint8Array([3, 4])), 14 | ], 15 | }; 16 | const opcode = new Concat(token, opcodeDefs.concat); 17 | opcode.execute(context); 18 | 19 | expect(context.stack.length).toEqual(1); 20 | expect(Array.from(context.stack[0]?.value)).toEqual([1, 2, 3, 4]); 21 | }); 22 | }); -------------------------------------------------------------------------------- /src/test/opcodes/cover.test.ts: -------------------------------------------------------------------------------- 1 | import { ITypedValue, makeBigInt, makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Cover } from "../../lib/opcodes/cover"; 4 | 5 | describe("cover opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = { 10 | operands: [ 11 | "3", 12 | ] 13 | }; 14 | const opcode = new Cover(token, opcodeDefs.cover); 15 | opcode.validateOperand(); 16 | 17 | const context: any = { 18 | stack: [ 19 | makeBigInt(BigInt(0)), 20 | makeBigInt(BigInt(1)), 21 | makeBigInt(BigInt(2)), 22 | makeBigInt(BigInt(3)), 23 | makeBigInt(BigInt(4)), 24 | ], 25 | }; 26 | opcode.execute(context); 27 | 28 | expect(context.stack.length).toEqual(5); 29 | expect(context.stack.map((el: ITypedValue) => Number(el.value))).toEqual([ 30 | 0, 4, 1, 2, 3 31 | ]); 32 | }); 33 | 34 | }); 35 | -------------------------------------------------------------------------------- /src/test/opcodes/div.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Div } from "../../lib/opcodes/div"; 4 | 5 | describe("div opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(10)), 13 | makeBigInt(BigInt(2)), 14 | ], 15 | }; 16 | const opcode = new Div(token, opcodeDefs["/"]); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Number(context.stack[0]?.value)).toEqual(5); 22 | }); 23 | }); -------------------------------------------------------------------------------- /src/test/opcodes/divmodw.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Divmodw } from "../../lib/opcodes/divmodw"; 4 | 5 | describe("divmodw opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(6)), 13 | makeBigInt(BigInt(0)), 14 | makeBigInt(BigInt(3)), 15 | makeBigInt(BigInt(0)), 16 | ], 17 | }; 18 | const opcode = new Divmodw(token, opcodeDefs.addw); 19 | opcode.validateContext(context); 20 | opcode.execute(context); 21 | 22 | expect(context.stack.length).toEqual(4); 23 | expect(Number(context.stack[0]!.value)).toEqual(0); 24 | expect(Number(context.stack[1]!.value)).toEqual(2); 25 | expect(Number(context.stack[2]!.value)).toEqual(0); 26 | expect(Number(context.stack[3]!.value)).toEqual(0); 27 | }); 28 | }); -------------------------------------------------------------------------------- /src/test/opcodes/dup.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Dup } from "../../lib/opcodes/dup"; 4 | 5 | describe("dup opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(3)), 13 | ], 14 | }; 15 | const opcode = new Dup(token, opcodeDefs.dup); 16 | opcode.execute(context); 17 | 18 | expect(context.stack.length).toEqual(2); 19 | expect(Number(context.stack[1]?.value)).toEqual(3); 20 | }); 21 | }); -------------------------------------------------------------------------------- /src/test/opcodes/dup2.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Dup2 } from "../../lib/opcodes/dup2"; 4 | 5 | describe("dup2 opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(1)), 13 | makeBigInt(BigInt(2)), 14 | ], 15 | }; 16 | const opcode = new Dup2(token, opcodeDefs.dup2); 17 | opcode.execute(context); 18 | 19 | expect(context.stack.length).toEqual(4); 20 | expect(Number(context.stack[2]?.value)).toEqual(1); 21 | expect(Number(context.stack[3]?.value)).toEqual(2); 22 | }); 23 | }); -------------------------------------------------------------------------------- /src/test/opcodes/ecdsa_verify.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { encodeAddress, stringToBytes } from "../../lib/convert"; 3 | import { opcodeDefs } from "../../lib/opcodes"; 4 | import { Ecdsa_verify } from "../../lib/opcodes/ecdsa_verify"; 5 | 6 | describe("ecdsa_verify opcode", () => { 7 | 8 | it ("can execute", () => { 9 | 10 | const token: any = { 11 | operands: [ 12 | "5", 13 | ], 14 | }; 15 | const opcode = new Ecdsa_verify(token, opcodeDefs.ecdsa_verify); 16 | 17 | const context: any = { 18 | stack: [ 19 | makeBytes(stringToBytes("iZWMx72KvU6Bw6sPAWQFL96YH+VMrBA0XKWD9XbZOZI=")), 20 | makeBytes(stringToBytes("iZWMx72KvU6Bw6sPAWQFL96YH+VMrBA0XKWD9XbZOZI=")), 21 | makeBytes(stringToBytes("if8ooA+32YZc4SQBvIDDY8tgTatPoq4IZ8Kr+We1t38LR2RuURmaVu9D4shbi4VvND87PUqq5/0vsNFEGIIEDA==")), 22 | makeBytes(encodeAddress("7JOPVEP3ABJUW5YZ5WFIONLPWTZ5MYX5HFK4K7JLGSIAG7RRB42MNLQ224")), 23 | makeBytes(encodeAddress("7JOPVEP3ABJUW5YZ5WFIONLPWTZ5MYX5HFK4K7JLGSIAG7RRB42MNLQ224")), 24 | ], 25 | }; 26 | 27 | opcode.validateOperand(); 28 | opcode.execute(context); 29 | 30 | expect(context.stack.length).toEqual(1); 31 | expect(Number(context.stack[0]?.value)).toEqual(0); 32 | }); 33 | }); -------------------------------------------------------------------------------- /src/test/opcodes/ed25519verify.test.ts: -------------------------------------------------------------------------------- 1 | import { decodeAddress } from "algosdk"; 2 | import { makeBytes } from "../../lib/context"; 3 | import { stringToBytes } from "../../lib/convert"; 4 | import { opcodeDefs } from "../../lib/opcodes"; 5 | import { Ed25519verify } from "../../lib/opcodes/ed25519verify"; 6 | 7 | describe("ed25519verify opcode", () => { 8 | 9 | it ("can execute", () => { 10 | 11 | const token: any = {}; 12 | const opcode = new Ed25519verify(token, opcodeDefs.ed25519verify); 13 | 14 | const context: any = { 15 | stack: [ 16 | makeBytes(stringToBytes("iZWMx72KvU6Bw6sPAWQFL96YH+VMrBA0XKWD9XbZOZI=")), 17 | makeBytes(stringToBytes("if8ooA+32YZc4SQBvIDDY8tgTatPoq4IZ8Kr+We1t38LR2RuURmaVu9D4shbi4VvND87PUqq5/0vsNFEGIIEDA==")), 18 | makeBytes(decodeAddress("7JOPVEP3ABJUW5YZ5WFIONLPWTZ5MYX5HFK4K7JLGSIAG7RRB42MNLQ224").publicKey), 19 | ], 20 | }; 21 | 22 | opcode.execute(context); 23 | 24 | expect(context.stack.length).toEqual(1); 25 | expect(Number(context.stack[0]?.value)).toEqual(0); 26 | }); 27 | }); -------------------------------------------------------------------------------- /src/test/opcodes/exp.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Exp } from "../../lib/opcodes/exp"; 4 | 5 | describe("exp opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(4)), 13 | makeBigInt(BigInt(2)), 14 | ], 15 | }; 16 | const opcode = new Exp(token, opcodeDefs.exp); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(context.stack[0].type === "bigint"); 22 | expect(Number(context.stack[0].value)).toEqual(16); 23 | }); 24 | }); -------------------------------------------------------------------------------- /src/test/opcodes/expw.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Expw } from "../../lib/opcodes/expw"; 4 | 5 | describe("expw opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(4)), 13 | makeBigInt(BigInt(2)), 14 | ], 15 | }; 16 | const opcode = new Expw(token, opcodeDefs.expw); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(2); 21 | expect(Number(context.stack[0].value)).toEqual(0); 22 | expect(Number(context.stack[1].value)).toEqual(16); 23 | }); 24 | }); -------------------------------------------------------------------------------- /src/test/opcodes/extract.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt, makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Extract } from "../../lib/opcodes/extract"; 4 | 5 | describe("extract opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = { 10 | operands: [ 11 | "1", 12 | "2" 13 | ] 14 | }; 15 | const opcode = new Extract(token, opcodeDefs.extract); 16 | opcode.validateOperand(); 17 | 18 | const context: any = { 19 | stack: [ 20 | makeBytes(new Uint8Array([1, 2, 3, 4])), 21 | ], 22 | }; 23 | opcode.execute(context); 24 | 25 | expect(context.stack.length).toEqual(1); 26 | expect(Array.from(context.stack[0]?.value)).toEqual([ 2, 3 ]); 27 | }); 28 | 29 | }); 30 | -------------------------------------------------------------------------------- /src/test/opcodes/extract3.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt, makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Extract3 } from "../../lib/opcodes/extract3"; 4 | 5 | describe("extract3 opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new Extract3(token, opcodeDefs.extract3); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(new Uint8Array([1, 2, 3, 4])), 15 | makeBigInt(BigInt(1)), 16 | makeBigInt(BigInt(2)), 17 | ], 18 | }; 19 | opcode.execute(context); 20 | 21 | expect(context.stack.length).toEqual(1); 22 | expect(Array.from(context.stack[0]?.value)).toEqual([ 2, 3 ]); 23 | }); 24 | 25 | }); 26 | -------------------------------------------------------------------------------- /src/test/opcodes/extractuint_x.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt, makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { ExtractUint_X } from "../../lib/opcodes/extractuint_x"; 4 | 5 | describe("extract_uintX opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new ExtractUint_X(token, opcodeDefs.extract_uint16, 2); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(new Uint8Array([1, 2, 3, 4])), 15 | makeBigInt(BigInt(1)), 16 | ], 17 | }; 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Number(context.stack[0]?.value)).toEqual(515); 22 | }); 23 | 24 | }); 25 | -------------------------------------------------------------------------------- /src/test/opcodes/gaids.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Gaids } from "../../lib/opcodes/gaids"; 4 | 5 | describe("gaids opcode", () => { 6 | 7 | it ("can execute", async () => { 8 | 9 | const txnIndex = 0; 10 | const token: any = { 11 | operands: [ 12 | txnIndex.toString(), 13 | ], 14 | }; 15 | const opcode = new Gaids(token, opcodeDefs.gaids); 16 | const context: any = { 17 | requireValue: (fieldPath: string) => { 18 | expect(fieldPath).toEqual(`gaid.${txnIndex}`); 19 | return makeBigInt(BigInt(3)); 20 | }, 21 | stack: [ 22 | makeBigInt(BigInt(0)), 23 | ], 24 | }; 25 | opcode.validateContext(context); 26 | await opcode.execute(context); 27 | 28 | expect(context.stack.length).toEqual(1); 29 | expect(Number(context.stack[0]?.value)).toEqual(3); 30 | }); 31 | 32 | it("throws when txn index is less than 0", () => { 33 | 34 | const token: any = {}; 35 | const opcode = new Gaids(token, opcodeDefs.gaids); 36 | 37 | const context: any = { 38 | stack: [ 39 | makeBigInt(BigInt(-1)), 40 | ], 41 | }; 42 | expect(() => opcode.validateContext(context)).toThrow(); 43 | }); 44 | 45 | }); -------------------------------------------------------------------------------- /src/test/opcodes/getbit.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt, makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { GetBit } from "../../lib/opcodes/getbit"; 4 | 5 | describe("getbit opcode", () => { 6 | 7 | it ("can get bit from number", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new GetBit(token, opcodeDefs.getbit); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBigInt(BigInt(6)), 15 | makeBigInt(BigInt(2)), 16 | ], 17 | }; 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Number(context.stack[0]?.value)).toEqual(1); 22 | }); 23 | 24 | it ("can get bit from bytes", () => { 25 | 26 | const token: any = {}; 27 | const opcode = new GetBit(token, opcodeDefs.getbit); 28 | 29 | const context: any = { 30 | stack: [ 31 | makeBytes(new Uint8Array([1, 255, 3, 4])), 32 | makeBigInt(BigInt(10)), 33 | ], 34 | }; 35 | opcode.execute(context); 36 | 37 | expect(context.stack.length).toEqual(1); 38 | expect(Number(context.stack[0]?.value)).toEqual(1); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /src/test/opcodes/getbyte.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt, makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { GetByte } from "../../lib/opcodes/getbyte"; 4 | 5 | describe("getbyte opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new GetByte(token, opcodeDefs.getbyte); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(new Uint8Array([1, 2, 3, 4])), 15 | makeBigInt(BigInt(2)), 16 | ], 17 | }; 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Number(context.stack[0]?.value)).toEqual(3); 22 | }); 23 | 24 | }); 25 | -------------------------------------------------------------------------------- /src/test/opcodes/global.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Global } from "../../lib/opcodes/global"; 4 | 5 | describe("global opcode", () => { 6 | 7 | it ("can execute", async () => { 8 | 9 | const token: any = { 10 | operands: [ 11 | "MinBalance" 12 | ], 13 | }; 14 | const context: any = { 15 | stack: [], 16 | requireValue: (fieldPath: string) => { 17 | expect(fieldPath).toEqual(`globals.MinBalance`); 18 | 19 | return makeBigInt(BigInt(18)); 20 | }, 21 | }; 22 | 23 | const opcode = new Global(token, opcodeDefs.global); 24 | opcode.validateOperand(); // Parses the operand. 25 | await opcode.execute(context); 26 | 27 | expect(context.stack.length).toEqual(1); 28 | expect(Number(context.stack[0]?.value)).toEqual(18); 29 | }); 30 | 31 | }); -------------------------------------------------------------------------------- /src/test/opcodes/gt.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Gt } from "../../lib/opcodes/gt"; 4 | 5 | describe("gt opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(2)), 13 | makeBigInt(BigInt(10)), 14 | ], 15 | }; 16 | const opcode = new Gt(token, opcodeDefs[">"]); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Number(context.stack[0]?.value)).toEqual(0); 22 | }); 23 | }); -------------------------------------------------------------------------------- /src/test/opcodes/gte.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Gte } from "../../lib/opcodes/gte"; 4 | 5 | describe("gte opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(2)), 13 | makeBigInt(BigInt(10)), 14 | ], 15 | }; 16 | const opcode = new Gte(token, opcodeDefs[">="]); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Number(context.stack[0]?.value)).toEqual(0); 22 | }); 23 | }); -------------------------------------------------------------------------------- /src/test/opcodes/gtxn.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Gtxn } from "../../lib/opcodes/gtxn"; 4 | 5 | describe("gtxn opcode", () => { 6 | 7 | it ("can execute", async () => { 8 | 9 | const token: any = { 10 | operands: [ 11 | "0", 12 | "Something", 13 | ], 14 | }; 15 | const opcode = new Gtxn(token, opcodeDefs.gtxn); 16 | 17 | opcode.validateOperand(); 18 | 19 | const context: any = { 20 | requireValue: (fieldPath: string) => { 21 | expect(fieldPath).toEqual(`gtxn.0.Something`); 22 | 23 | return makeBigInt(BigInt(42)); 24 | }, 25 | stack: [], 26 | }; 27 | await opcode.execute(context); 28 | 29 | expect(context.stack.length).toEqual(1); 30 | expect(Number(context.stack[0]?.value)).toEqual(42); 31 | }); 32 | 33 | it("throws when operand is not an int", () => { 34 | 35 | const token: any = { 36 | operands: [ 37 | "not-an-int", 38 | "Fee", 39 | ], 40 | }; 41 | const opcode = new Gtxn(token, opcodeDefs.gtxn); 42 | expect(() => opcode.validateOperand()).toThrow(); 43 | }); 44 | 45 | }); -------------------------------------------------------------------------------- /src/test/opcodes/gtxna.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Gtxna } from "../../lib/opcodes/gtxna"; 4 | 5 | describe("gtxna opcode", () => { 6 | 7 | it ("can execute", async () => { 8 | 9 | const token: any = { 10 | operands: [ 11 | "0", 12 | "Something", 13 | "0", 14 | ], 15 | }; 16 | const opcode = new Gtxna(token, opcodeDefs.gtxna); 17 | 18 | const context: any = { 19 | requireValue: (fieldPath: string) => { 20 | expect(fieldPath).toEqual(`gtxn.0.Something.0`); 21 | 22 | return makeBigInt(BigInt(42)); 23 | }, 24 | stack: [], 25 | }; 26 | opcode.validateOperand(); 27 | opcode.validateContext(context); 28 | await opcode.execute(context); 29 | 30 | expect(context.stack.length).toEqual(1); 31 | expect(Number(context.stack[0]?.value)).toEqual(42); 32 | }); 33 | 34 | }); -------------------------------------------------------------------------------- /src/test/opcodes/gtxnas.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Gtxnas } from "../../lib/opcodes/gtxnas"; 4 | 5 | describe("gtxnas opcode", () => { 6 | 7 | it ("can execute", async () => { 8 | 9 | const token: any = { 10 | operands: [ 11 | "0", 12 | "Something", 13 | ], 14 | }; 15 | const opcode = new Gtxnas(token, opcodeDefs.gtxnas); 16 | opcode.validateOperand(); 17 | 18 | const context: any = { 19 | requireValue: (fieldPath: string) => { 20 | expect(fieldPath).toEqual(`gtxn.0.Something.0`); 21 | 22 | return makeBigInt(BigInt(42)); 23 | }, 24 | stack: [ 25 | makeBigInt(BigInt(0)) 26 | ], 27 | }; 28 | opcode.validateContext(context); 29 | await opcode.execute(context); 30 | 31 | expect(context.stack.length).toEqual(1); 32 | expect(Number(context.stack[0]?.value)).toEqual(42); 33 | }); 34 | 35 | }); -------------------------------------------------------------------------------- /src/test/opcodes/gtxns.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Gtxns } from "../../lib/opcodes/gtxns"; 4 | 5 | describe("gtxns opcode", () => { 6 | 7 | it ("can execute", async () => { 8 | 9 | const token: any = { 10 | operands: [ 11 | "Something" 12 | ], 13 | }; 14 | const opcode = new Gtxns(token, opcodeDefs.gtxns); 15 | opcode.validateOperand(); 16 | 17 | const context: any = { 18 | requireValue: (fieldPath: string) => { 19 | expect(fieldPath).toEqual(`gtxn.0.Something`); 20 | 21 | return makeBigInt(BigInt(42)); 22 | }, 23 | stack: [ 24 | makeBigInt(BigInt(0)), 25 | ], 26 | }; 27 | opcode.validateContext(context); 28 | await opcode.execute(context); 29 | 30 | expect(context.stack.length).toEqual(1); 31 | expect(Number(context.stack[0]?.value)).toEqual(42); 32 | }); 33 | 34 | 35 | }); -------------------------------------------------------------------------------- /src/test/opcodes/gtxnsa.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Gtxnsa } from "../../lib/opcodes/gtxnsa"; 4 | 5 | describe("gtxnsa opcode", () => { 6 | 7 | it ("can execute", async () => { 8 | 9 | const token: any = { 10 | operands: [ 11 | "Something", 12 | "0", 13 | ], 14 | }; 15 | const opcode = new Gtxnsa(token, opcodeDefs.gtxnsa); 16 | opcode.validateOperand(); 17 | 18 | const context: any = { 19 | requireValue: (fieldPath: string) => { 20 | expect(fieldPath).toEqual(`gtxn.0.Something.0`); 21 | 22 | return makeBigInt(BigInt(42)); 23 | }, 24 | stack: [ 25 | makeBigInt(BigInt(0)) 26 | ], 27 | }; 28 | opcode.validateContext(context); 29 | await opcode.execute(context); 30 | 31 | expect(context.stack.length).toEqual(1); 32 | expect(Number(context.stack[0]?.value)).toEqual(42); 33 | }); 34 | 35 | }); -------------------------------------------------------------------------------- /src/test/opcodes/gtxnsas.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Gtxnsas } from "../../lib/opcodes/gtxnsas"; 4 | 5 | describe("gtxnsas opcode", () => { 6 | 7 | it ("can execute", async () => { 8 | 9 | const token: any = { 10 | operands: [ 11 | "Something", 12 | ], 13 | }; 14 | const opcode = new Gtxnsas(token, opcodeDefs.gtxnsas); 15 | opcode.validateOperand(); 16 | 17 | const context: any = { 18 | requireValue: (fieldPath: string) => { 19 | expect(fieldPath).toEqual(`gtxn.0.Something.0`); 20 | 21 | return makeBigInt(BigInt(42)); 22 | }, 23 | stack: [ 24 | makeBigInt(BigInt(0)), 25 | makeBigInt(BigInt(0)), 26 | ], 27 | }; 28 | opcode.validateContext(context); 29 | await opcode.execute(context); 30 | 31 | expect(context.stack.length).toEqual(1); 32 | expect(Number(context.stack[0]?.value)).toEqual(42); 33 | }); 34 | 35 | }); -------------------------------------------------------------------------------- /src/test/opcodes/intc.test.ts: -------------------------------------------------------------------------------- 1 | import { opcodeDefs } from "../../lib/opcodes"; 2 | import { Intc } from "../../lib/opcodes/intc"; 3 | 4 | describe("intc opcode", () => { 5 | 6 | it ("can execute", () => { 7 | 8 | const token: any = { 9 | operands: [ 10 | "2" 11 | ], 12 | }; 13 | const opcode = new Intc(token, opcodeDefs.intc); 14 | opcode.validateOperand(); // Parses the operand. 15 | 16 | const context: any = { 17 | stack: [], 18 | intcblock: [ 19 | BigInt(1), 20 | BigInt(2), 21 | BigInt(3), 22 | ], 23 | }; 24 | opcode.execute(context); 25 | 26 | expect(context.stack.length).toEqual(1); 27 | expect(Number(context.stack[0]?.value)).toEqual(3); 28 | }); 29 | 30 | it("throws when operand is not an int", () => { 31 | 32 | const token: any = { 33 | operands: [ 34 | "xxx" 35 | ], 36 | }; 37 | const opcode = new Intc(token, opcodeDefs.intc); 38 | 39 | expect(() => opcode.validateOperand()).toThrow(); 40 | }); 41 | 42 | }); -------------------------------------------------------------------------------- /src/test/opcodes/intc_X.test.ts: -------------------------------------------------------------------------------- 1 | import { opcodeDefs } from "../../lib/opcodes"; 2 | import { Intc_X } from "../../lib/opcodes/intc_X"; 3 | 4 | describe("intc_X opcode", () => { 5 | 6 | it ("can execute with index 0", () => { 7 | 8 | const token: any = {}; 9 | const opcode = new Intc_X(token, opcodeDefs.intc_0, 0); 10 | 11 | const context: any = { 12 | stack: [], 13 | intcblock: [ 14 | BigInt(1), 15 | ], 16 | }; 17 | opcode.execute(context); 18 | 19 | expect(context.stack.length).toEqual(1); 20 | expect(Number(context.stack[0].value)).toEqual(1); 21 | }); 22 | 23 | it ("can execute with index 2", () => { 24 | 25 | const token: any = {}; 26 | const opcode = new Intc_X(token, opcodeDefs.intc_2, 2); 27 | 28 | const context: any = { 29 | stack: [], 30 | intcblock: [ 31 | BigInt(1), 32 | BigInt(2), 33 | BigInt(3), 34 | ], 35 | }; 36 | opcode.execute(context); 37 | 38 | expect(context.stack.length).toEqual(1); 39 | expect(Number(context.stack[0]?.value)).toEqual(3); 40 | }); 41 | 42 | }); -------------------------------------------------------------------------------- /src/test/opcodes/intcblock.test.ts: -------------------------------------------------------------------------------- 1 | import { opcodeDefs } from "../../lib/opcodes"; 2 | import { Intcblock } from "../../lib/opcodes/intcblock"; 3 | 4 | describe("intcblock opcode", () => { 5 | 6 | it ("can execute", () => { 7 | 8 | const token: any = { 9 | operands: [ 10 | "1", 11 | "2", 12 | "3", 13 | ], 14 | }; 15 | 16 | const opcode = new Intcblock(token, opcodeDefs.intcblock); 17 | opcode.validateOperand(); // Parses the operand. 18 | 19 | const context: any = { 20 | stack: [], 21 | }; 22 | opcode.execute(context); 23 | 24 | expect(context.intcblock.length).toEqual(3); 25 | expect(Number(context.intcblock[0])).toEqual(1); 26 | expect(Number(context.intcblock[1])).toEqual(2); 27 | expect(Number(context.intcblock[2])).toEqual(3); 28 | }); 29 | 30 | it("throws when operand is not an int", () => { 31 | 32 | const token: any = { 33 | operands: [ 34 | "xxx" 35 | ], 36 | }; 37 | const opcode = new Intcblock(token, opcodeDefs.intcblock); 38 | 39 | expect(() => opcode.validateOperand()).toThrow(); 40 | }); 41 | 42 | }); -------------------------------------------------------------------------------- /src/test/opcodes/itob.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Itob } from "../../lib/opcodes/itob"; 4 | 5 | describe("itob opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt("0x1234567812345678")), 13 | ], 14 | }; 15 | const opcode = new Itob(token, opcodeDefs.itob); 16 | opcode.validateContext(context); 17 | opcode.execute(context); 18 | 19 | expect(context.stack.length).toEqual(1); 20 | expect(Array.from(context.stack[0]?.value)).toEqual([ 21 | 18, 52, 86, 120, 22 | 18, 52, 86, 120 23 | ]); 24 | }); 25 | }); -------------------------------------------------------------------------------- /src/test/opcodes/itxn_begin.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { ItxnBegin } from "../../lib/opcodes/itxn_begin"; 4 | 5 | describe("itxn_begin opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new ItxnBegin(token, opcodeDefs.itxn_begin); 11 | 12 | const context: any = {}; 13 | opcode.execute(context); 14 | 15 | expect(context.itxn).toEqual({}); 16 | }); 17 | 18 | }); -------------------------------------------------------------------------------- /src/test/opcodes/itxn_field.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { ItxnField } from "../../lib/opcodes/itxn_field"; 4 | 5 | describe("itxn_field opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = { 10 | operands: [ 11 | "Something" 12 | ], 13 | }; 14 | const context: any = { 15 | stack: [ 16 | makeBigInt(BigInt(42)), 17 | ], 18 | itxn: { 19 | }, 20 | }; 21 | const opcode = new ItxnField(token, opcodeDefs.itxn_field); 22 | opcode.validateOperand(); 23 | opcode.execute(context); 24 | 25 | expect(Number(context.itxn.Something.value)).toEqual(42); 26 | }); 27 | 28 | it("throws when inner transaction not started", () => { 29 | 30 | const token: any = { 31 | operands: [ 32 | "Something" 33 | ], 34 | }; 35 | const opcode = new ItxnField(token, opcodeDefs.itxn_field); 36 | opcode.validateOperand(); 37 | 38 | const context: any = { 39 | stack: [ 40 | makeBigInt(BigInt(42)), 41 | ], 42 | }; 43 | expect(() => opcode.execute(context)).toThrow(); 44 | }); 45 | 46 | }); -------------------------------------------------------------------------------- /src/test/opcodes/itxn_next.test.ts: -------------------------------------------------------------------------------- 1 | import { opcodeDefs } from "../../lib/opcodes"; 2 | import { ItxnNext } from "../../lib/opcodes/itxn_next"; 3 | 4 | describe("itxn_next opcode", () => { 5 | 6 | it ("can execute", () => { 7 | 8 | const token: any = {}; 9 | const opcode = new ItxnNext(token, opcodeDefs.itxn_next); 10 | 11 | const itxnInProgress = {}; 12 | const context: any = { 13 | itxn: itxnInProgress, 14 | }; 15 | opcode.execute(context); 16 | 17 | expect(context.submittedItxns.length).toEqual(1); 18 | expect(context.submittedItxns[0]).toEqual(itxnInProgress); 19 | expect(context.itxn).toEqual({}); 20 | expect(context.itxn).not.toBe(itxnInProgress); 21 | }); 22 | 23 | it ("throws when inner transaction is not started", () => { 24 | 25 | const token: any = {}; 26 | const opcode = new ItxnNext(token, opcodeDefs.itxn_next); 27 | 28 | const context: any = {}; 29 | expect(() => opcode.execute(context)).toThrow(); 30 | }); 31 | 32 | 33 | }); -------------------------------------------------------------------------------- /src/test/opcodes/itxn_submit.test.ts: -------------------------------------------------------------------------------- 1 | import { opcodeDefs } from "../../lib/opcodes"; 2 | import { ItxnSubmit } from "../../lib/opcodes/itxn_submit"; 3 | 4 | describe("itxn_submit opcode", () => { 5 | 6 | it ("can execute", () => { 7 | 8 | const token: any = {}; 9 | const opcode = new ItxnSubmit(token, opcodeDefs.itxn_submit); 10 | 11 | const itxnInProgress = {}; 12 | const context: any = { 13 | itxn: itxnInProgress, 14 | }; 15 | opcode.execute(context); 16 | 17 | expect(context.submittedItxns.length).toEqual(1); 18 | expect(context.submittedItxns[0]).toEqual(itxnInProgress); 19 | 20 | expect(context.itxn).toBeUndefined(); 21 | }); 22 | 23 | it ("throws when inner transaction is not started", () => { 24 | 25 | const token: any = {}; 26 | const opcode = new ItxnSubmit(token, opcodeDefs.itxn_submit); 27 | 28 | const context: any = {}; 29 | expect(() => opcode.execute(context)).toThrow(); 30 | }); 31 | }); -------------------------------------------------------------------------------- /src/test/opcodes/keccak256.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Keccak256 } from "../../lib/opcodes/keccak256"; 4 | 5 | describe("keccak256 opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new Keccak256(token, opcodeDefs.keccak256); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(new Uint8Array([1, 2, 3, 4, 5])), 15 | ], 16 | }; 17 | 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Array.from(context.stack[0]?.value)).toEqual([ 22 | 125, 135, 197, 234, 117, 247, 55, 139, 23 | 183, 1, 228, 4, 197, 6, 57, 22, 24 | 26, 243, 239, 246, 98, 147, 233, 243, 25 | 117, 181, 241, 126, 181, 4, 118, 244 26 | ]); 27 | }); 28 | }); -------------------------------------------------------------------------------- /src/test/opcodes/len.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Len } from "../../lib/opcodes/len"; 4 | 5 | describe("len opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBytes(new Uint8Array([1, 2, 3, 4])), 13 | ], 14 | }; 15 | const opcode = new Len(token, opcodeDefs.len); 16 | opcode.execute(context); 17 | 18 | expect(context.stack.length).toEqual(1); 19 | expect(Number(context.stack[0]?.value)).toEqual(4); 20 | }); 21 | }); -------------------------------------------------------------------------------- /src/test/opcodes/log.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt, makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Log } from "../../lib/opcodes/log"; 4 | 5 | describe("log opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new Log(token, opcodeDefs.dup); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(new Uint8Array([1, 2, 3, 4])), 15 | ], 16 | logState: [], 17 | }; 18 | opcode.execute(context); 19 | 20 | expect(context.logState.length).toEqual(1); 21 | expect(context.logState[0]).toEqual(new Uint8Array([1, 2, 3, 4])); 22 | }); 23 | }); -------------------------------------------------------------------------------- /src/test/opcodes/lt.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Lt } from "../../lib/opcodes/lt"; 4 | 5 | describe("lt opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(2)), 13 | makeBigInt(BigInt(10)), 14 | ], 15 | }; 16 | const opcode = new Lt(token, opcodeDefs["<"]); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Number(context.stack[0]?.value)).toEqual(1); 22 | }); 23 | }); -------------------------------------------------------------------------------- /src/test/opcodes/lte.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Lte } from "../../lib/opcodes/lte"; 4 | 5 | describe("lte opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(2)), 13 | makeBigInt(BigInt(10)), 14 | ], 15 | }; 16 | const opcode = new Lte(token, opcodeDefs["<="]); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Number(context.stack[0]?.value)).toEqual(1); 22 | }); 23 | }); -------------------------------------------------------------------------------- /src/test/opcodes/min_balance.test.ts: -------------------------------------------------------------------------------- 1 | import {makeBigInt, makeBytes } from "../../lib/context"; 2 | import { encodeAddress } from "../../lib/convert"; 3 | import { opcodeDefs } from "../../lib/opcodes"; 4 | import { MinBalance } from "../../lib/opcodes/min_balance"; 5 | 6 | describe("min_balance opcode", () => { 7 | 8 | it ("can execute", async () => { 9 | 10 | const addr = "john"; 11 | const token: any = {}; 12 | const opcode = new MinBalance(token, opcodeDefs.min_balance); 13 | 14 | const context: any = { 15 | requireValue: async (fieldPath: string) => { 16 | expect(fieldPath).toEqual(`accounts.${addr}.minBalance`); 17 | return 12; 18 | }, 19 | stack: [ 20 | makeBytes(encodeAddress(addr)), 21 | ], 22 | }; 23 | await opcode.execute(context); 24 | 25 | expect(context.stack.length).toEqual(1); 26 | expect(Number(context.stack[0]?.value)).toEqual(12); 27 | }); 28 | 29 | }); -------------------------------------------------------------------------------- /src/test/opcodes/minus.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Minus } from "../../lib/opcodes/minus"; 4 | 5 | describe("minus opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(6)), 13 | makeBigInt(BigInt(4)), 14 | ], 15 | }; 16 | const opcode = new Minus(token, opcodeDefs["-"]); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Number(context.stack[0]?.value)).toEqual(2); 22 | }); 23 | 24 | it("throws on underflow", () => { 25 | 26 | const token: any = {}; 27 | const context: any = { 28 | stack: [ 29 | makeBigInt(BigInt(4)), 30 | makeBigInt(BigInt(6)), 31 | ], 32 | }; 33 | const opcode = new Minus(token, opcodeDefs["-"]); 34 | opcode.validateContext(context); 35 | expect(() => opcode.execute(context)).toThrow(); 36 | }); 37 | }); -------------------------------------------------------------------------------- /src/test/opcodes/mod.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Mod } from "../../lib/opcodes/mod"; 4 | 5 | describe("mod opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(4)), 13 | makeBigInt(BigInt(3)), 14 | ], 15 | }; 16 | const opcode = new Mod(token, opcodeDefs["%"]); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Number(context.stack[0]?.value)).toEqual(1); 22 | }); 23 | }); -------------------------------------------------------------------------------- /src/test/opcodes/mul.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Mul } from "../../lib/opcodes/mul"; 4 | 5 | describe("mul opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(10)), 13 | makeBigInt(BigInt(2)), 14 | ], 15 | }; 16 | const opcode = new Mul(token, opcodeDefs["*"]); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Number(context.stack[0]?.value)).toEqual(20); 22 | }); 23 | }); -------------------------------------------------------------------------------- /src/test/opcodes/mulw.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Mulw } from "../../lib/opcodes/mulw"; 4 | 5 | describe("mul opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(10)), 13 | makeBigInt(BigInt(2)), 14 | ], 15 | }; 16 | const opcode = new Mulw(token, opcodeDefs.mulw); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(2); 21 | expect(Number(context.stack[0]?.value)).toEqual(0); 22 | expect(Number(context.stack[1]?.value)).toEqual(20); 23 | }); 24 | }); -------------------------------------------------------------------------------- /src/test/opcodes/not.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Not } from "../../lib/opcodes/not"; 4 | 5 | describe("not opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(3)), 13 | ], 14 | }; 15 | const opcode = new Not(token, opcodeDefs["!"]); 16 | opcode.execute(context); 17 | 18 | expect(context.stack.length).toEqual(1); 19 | expect(Number(context.stack[0]?.value)).toEqual(0); 20 | }); 21 | }); -------------------------------------------------------------------------------- /src/test/opcodes/or.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Or } from "../../lib/opcodes/or"; 4 | 5 | describe("or opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(3)), 13 | makeBigInt(BigInt(0)), 14 | ], 15 | }; 16 | const opcode = new Or(token, opcodeDefs["||"]); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Number(context.stack[0]?.value)).toEqual(1); 22 | }); 23 | }); -------------------------------------------------------------------------------- /src/test/opcodes/pop.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Pop } from "../../lib/opcodes/pop"; 4 | 5 | describe("pop opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(3)), 13 | ], 14 | }; 15 | const opcode = new Pop(token, opcodeDefs.pop); 16 | opcode.execute(context); 17 | 18 | expect(context.stack.length).toEqual(0); 19 | }); 20 | }); -------------------------------------------------------------------------------- /src/test/opcodes/retsub.test.ts: -------------------------------------------------------------------------------- 1 | import { opcodeDefs } from "../../lib/opcodes"; 2 | import { Retsub } from "../../lib/opcodes/retsub"; 3 | 4 | describe("retsub opcode", () => { 5 | 6 | it("throws when nothing on the callstack", () => { 7 | 8 | const token: any = {}; 9 | const opcode = new Retsub(token, opcodeDefs.b) 10 | 11 | const context: any = { 12 | callstack: [], 13 | }; 14 | expect(() => opcode.validateContext(context)).toThrow(); 15 | }); 16 | 17 | it ("can execute", () => { 18 | 19 | const token: any = {}; 20 | const opcode = new Retsub(token, opcodeDefs.b); 21 | 22 | const context: any = { 23 | callstack: [ 15 ], 24 | }; 25 | expect(opcode.execute(context)).toEqual(15); 26 | expect(context.callstack.length).toEqual(0); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/test/opcodes/return.test.ts: -------------------------------------------------------------------------------- 1 | import { opcodeDefs } from "../../lib/opcodes"; 2 | import { Return } from "../../lib/opcodes/return"; 3 | 4 | describe("return opcode", () => { 5 | 6 | it ("can execute", () => { 7 | 8 | const token: any = {}; 9 | const opcode = new Return(token, opcodeDefs.return); 10 | 11 | const context: any = {}; 12 | opcode.execute(context); 13 | expect(context.finished).toEqual(true); 14 | }); 15 | }); -------------------------------------------------------------------------------- /src/test/opcodes/select.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Select } from "../../lib/opcodes/select"; 4 | 5 | describe("select opcode", () => { 6 | 7 | it ("when condition is non-zero push b", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new Select(token, opcodeDefs.select); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBigInt(BigInt(1)), // A 15 | makeBigInt(BigInt(2)), // B 16 | makeBigInt(BigInt(10)), 17 | ], 18 | }; 19 | opcode.execute(context); 20 | 21 | expect(context.stack.length).toEqual(1); 22 | expect(Number(context.stack[0]?.value)).toEqual(2); 23 | }); 24 | 25 | it ("when condition is zero push a", () => { 26 | 27 | const token: any = {}; 28 | const opcode = new Select(token, opcodeDefs.select); 29 | 30 | const context: any = { 31 | stack: [ 32 | makeBigInt(BigInt(1)), // A 33 | makeBigInt(BigInt(2)), // B 34 | makeBigInt(BigInt(0)), 35 | ], 36 | }; 37 | opcode.execute(context); 38 | 39 | expect(context.stack.length).toEqual(1); 40 | expect(Number(context.stack[0]?.value)).toEqual(1); 41 | }); 42 | }); 43 | 44 | -------------------------------------------------------------------------------- /src/test/opcodes/setbit.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt, makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { SetBit } from "../../lib/opcodes/setbit"; 4 | 5 | describe("setbit opcode", () => { 6 | 7 | it ("can set bit on number", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new SetBit(token, opcodeDefs.setbit); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBigInt(BigInt(0)), 15 | makeBigInt(BigInt(3)), 16 | makeBigInt(BigInt(1)), 17 | ], 18 | }; 19 | opcode.execute(context); 20 | 21 | expect(context.stack.length).toEqual(1); 22 | expect(Number(context.stack[0]?.value)).toEqual(8); 23 | }); 24 | 25 | it ("can set bit from bytes", () => { 26 | 27 | const token: any = {}; 28 | const opcode = new SetBit(token, opcodeDefs.setbit); 29 | 30 | const context: any = { 31 | stack: [ 32 | makeBytes(new Uint8Array([1, 0, 3, 4])), 33 | makeBigInt(BigInt(10)), 34 | makeBigInt(BigInt(1)), 35 | ], 36 | }; 37 | opcode.execute(context); 38 | 39 | expect(context.stack.length).toEqual(1); 40 | expect(Array.from(context.stack[0]?.value)).toEqual([ 41 | 1, 32, 3, 4 42 | ]); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/test/opcodes/setbyte.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt, makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { SetByte } from "../../lib/opcodes/setbyte"; 4 | 5 | describe("setbyte opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new SetByte(token, opcodeDefs.setbyte); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(new Uint8Array([1, 2, 3, 4])), 15 | makeBigInt(BigInt(2)), 16 | makeBigInt(BigInt(12)), 17 | ], 18 | }; 19 | opcode.execute(context); 20 | 21 | expect(context.stack.length).toEqual(1); 22 | expect(Array.from(context.stack[0]?.value)).toEqual([ 23 | 1, 24 | 2, 25 | 12, 26 | 4, 27 | ]); 28 | }); 29 | 30 | }); 31 | -------------------------------------------------------------------------------- /src/test/opcodes/sha256.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Sha256 } from "../../lib/opcodes/sha256"; 4 | 5 | describe("sha256 opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new Sha256(token, opcodeDefs.sha256); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(new Uint8Array([1, 2, 3, 4, 5])), 15 | ], 16 | }; 17 | 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Array.from(context.stack[0]?.value)).toEqual([ 22 | 116, 248, 31, 225, 103, 217, 155, 23 | 76, 180, 29, 109, 12, 205, 168, 24 | 34, 120, 202, 238, 159, 62, 47, 25 | 37, 213, 229, 163, 147, 111, 243, 26 | 220, 236, 96, 208 27 | ]); 28 | }); 29 | }); -------------------------------------------------------------------------------- /src/test/opcodes/sha512_256.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Sha512_256 } from "../../lib/opcodes/sha512_256"; 4 | 5 | describe("sha512_256 opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new Sha512_256(token, opcodeDefs.sha512_256); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(new Uint8Array([1, 2, 3, 4, 5])), 15 | ], 16 | }; 17 | 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Array.from(context.stack[0]?.value)).toEqual([ 22 | 65, 108, 169, 188, 129, 249, 37, 81, 23 | 162, 91, 228, 162, 163, 63, 230, 143, 24 | 151, 41, 154, 40, 12, 3, 143, 249, 25 | 154, 242, 103, 173, 89, 201, 154, 235 26 | ]); 27 | }); 28 | }); -------------------------------------------------------------------------------- /src/test/opcodes/shl.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Shl } from "../../lib/opcodes/shl"; 4 | 5 | describe("shl opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(3)), 13 | makeBigInt(BigInt(4)), 14 | ], 15 | }; 16 | const opcode = new Shl(token, opcodeDefs["+"]); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(context.stack[0].type === "bigint"); 22 | expect(Number(context.stack[0].value)).toEqual(48); 23 | }); 24 | }); -------------------------------------------------------------------------------- /src/test/opcodes/shr.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Shr } from "../../lib/opcodes/shr"; 4 | 5 | describe("shr opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(48)), 13 | makeBigInt(BigInt(4)), 14 | ], 15 | }; 16 | const opcode = new Shr(token, opcodeDefs["+"]); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(context.stack[0].type === "bigint"); 22 | expect(Number(context.stack[0].value)).toEqual(3); 23 | }); 24 | }); -------------------------------------------------------------------------------- /src/test/opcodes/sqrt.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Sqrt } from "../../lib/opcodes/sqrt"; 4 | 5 | describe("sqrt opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(16)), 13 | ], 14 | }; 15 | const opcode = new Sqrt(token, opcodeDefs.sqrt); 16 | opcode.execute(context); 17 | 18 | expect(context.stack.length).toEqual(1); 19 | expect(Number(context.stack[0]?.value)).toEqual(4); 20 | }); 21 | }); -------------------------------------------------------------------------------- /src/test/opcodes/substring.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Substring } from "../../lib/opcodes/substring"; 4 | 5 | describe("substring opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = { 10 | operands: [ 11 | "1", 12 | "3", 13 | ], 14 | }; 15 | const opcode = new Substring(token, opcodeDefs.substring); 16 | opcode.validateOperand(); 17 | 18 | const context: any = { 19 | stack: [ 20 | makeBytes(new Uint8Array([1, 2, 3, 4])), 21 | ], 22 | }; 23 | opcode.execute(context); 24 | 25 | expect(context.stack.length).toEqual(1); 26 | expect(Array.from(context.stack[0]?.value)).toEqual([2, 3]); 27 | }); 28 | }); -------------------------------------------------------------------------------- /src/test/opcodes/substring3.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt, makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Substring3 } from "../../lib/opcodes/substring3"; 4 | 5 | describe("substring3 opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new Substring3(token, opcodeDefs.substring3); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBytes(new Uint8Array([1, 2, 3, 4])), 15 | makeBigInt(BigInt(1)), 16 | makeBigInt(BigInt(3)), 17 | ], 18 | }; 19 | opcode.validateContext(context); 20 | opcode.execute(context); 21 | 22 | expect(context.stack.length).toEqual(1); 23 | expect(Array.from(context.stack[0]?.value)).toEqual([2, 3]); 24 | }); 25 | }); -------------------------------------------------------------------------------- /src/test/opcodes/swap.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Swap } from "../../lib/opcodes/swap"; 4 | 5 | describe("swap opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const opcode = new Swap(token, opcodeDefs.swap); 11 | 12 | const context: any = { 13 | stack: [ 14 | makeBigInt(BigInt(1)), 15 | makeBigInt(BigInt(2)), 16 | ], 17 | }; 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(2); 21 | expect(Number(context.stack[0]?.value)).toEqual(2); 22 | expect(Number(context.stack[1]?.value)).toEqual(1); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/test/opcodes/txn.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Txn } from "../../lib/opcodes/txn"; 4 | 5 | describe("txn opcode", () => { 6 | 7 | it ("can execute", async () => { 8 | 9 | const token: any = { 10 | operands: [ 11 | "Something" 12 | ], 13 | }; 14 | const context: any = { 15 | requireValue: (fieldPath: string) => { 16 | expect(fieldPath).toEqual(`txn.Something`); 17 | 18 | return makeBigInt(BigInt(42)); 19 | }, 20 | stack: [], 21 | }; 22 | const opcode = new Txn(token, opcodeDefs.txn); 23 | opcode.validateOperand(); // Parses the operand. 24 | await opcode.execute(context); 25 | 26 | expect(context.stack.length).toEqual(1); 27 | expect(Number(context.stack[0]?.value)).toEqual(42); 28 | }); 29 | 30 | }); -------------------------------------------------------------------------------- /src/test/opcodes/txna.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Txna } from "../../lib/opcodes/txna"; 4 | 5 | describe("txna opcode", () => { 6 | 7 | it ("can execute", async () => { 8 | 9 | const token: any = { 10 | operands: [ 11 | "Something", 12 | "1", 13 | ], 14 | }; 15 | const opcode = new Txna(token, opcodeDefs.txna); 16 | 17 | const context: any = { 18 | requireValue: (fieldPath: string) => { 19 | expect(fieldPath).toEqual(`txn.Something.1`); 20 | 21 | return makeBigInt(BigInt(2)); 22 | }, 23 | stack: [], 24 | }; 25 | opcode.validateOperand(); 26 | await opcode.execute(context); 27 | 28 | expect(context.stack.length).toEqual(1); 29 | expect(Number(context.stack[0]?.value)).toEqual(2); 30 | }); 31 | 32 | }); -------------------------------------------------------------------------------- /src/test/opcodes/txnas.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Txnas } from "../../lib/opcodes/txnas"; 4 | 5 | describe("txnas opcode", () => { 6 | 7 | it ("can execute", async () => { 8 | 9 | const token: any = { 10 | operands: [ 11 | "Something", 12 | ], 13 | }; 14 | const opcode = new Txnas(token, opcodeDefs.txnas); 15 | opcode.validateOperand(); 16 | 17 | const context: any = { 18 | requireValue: (fieldPath: string) => { 19 | expect(fieldPath).toEqual(`txn.Something.0`); 20 | 21 | return makeBigInt(BigInt(8)); 22 | }, 23 | stack: [ 24 | makeBigInt(BigInt(0)), 25 | ], 26 | }; 27 | opcode.validateContext(context); 28 | await opcode.execute(context); 29 | 30 | expect(context.stack.length).toEqual(1); 31 | expect(Number(context.stack[0]?.value)).toEqual(8); 32 | }); 33 | 34 | }); -------------------------------------------------------------------------------- /src/test/opcodes/uncover.test.ts: -------------------------------------------------------------------------------- 1 | import { ITypedValue, makeBigInt, makeBytes } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Uncover } from "../../lib/opcodes/uncover"; 4 | 5 | describe("uncover opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = { 10 | operands: [ 11 | "3", 12 | ] 13 | }; 14 | const opcode = new Uncover(token, opcodeDefs.uncover); 15 | opcode.validateOperand(); 16 | 17 | const context: any = { 18 | stack: [ 19 | makeBigInt(BigInt(0)), 20 | makeBigInt(BigInt(1)), 21 | makeBigInt(BigInt(2)), 22 | makeBigInt(BigInt(3)), 23 | makeBigInt(BigInt(4)), 24 | ], 25 | }; 26 | opcode.execute(context); 27 | 28 | expect(context.stack.length).toEqual(5); 29 | expect(context.stack.map((el: ITypedValue) => Number(el.value))).toEqual([ 30 | 0, 2, 3, 4, 1 31 | ]); 32 | }); 33 | 34 | }); 35 | -------------------------------------------------------------------------------- /src/test/opcodes/xor.test.ts: -------------------------------------------------------------------------------- 1 | import { makeBigInt } from "../../lib/context"; 2 | import { opcodeDefs } from "../../lib/opcodes"; 3 | import { Band } from "../../lib/opcodes/band"; 4 | 5 | describe("exclusive or opcode", () => { 6 | 7 | it ("can execute", () => { 8 | 9 | const token: any = {}; 10 | const context: any = { 11 | stack: [ 12 | makeBigInt(BigInt(3)), 13 | makeBigInt(BigInt(7)), 14 | ], 15 | }; 16 | const opcode = new Band(token, opcodeDefs["^"]); 17 | opcode.validateContext(context); 18 | opcode.execute(context); 19 | 20 | expect(context.stack.length).toEqual(1); 21 | expect(Number(context.stack[0]?.value)).toEqual(3); 22 | }); 23 | }); -------------------------------------------------------------------------------- /src/test/parser.test.ts: -------------------------------------------------------------------------------- 1 | import { parse } from "../lib/parser"; 2 | 3 | describe("teal parser", () => { 4 | 5 | it("throws error when parsing an unrecognised opcode", () => { 6 | 7 | expect(() => parse("foobar")).toThrow(); 8 | }); 9 | 10 | it("throws when operands are not valid", () => { 11 | 12 | expect(() => parse("int")).toThrow(); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /stamp-version.js: -------------------------------------------------------------------------------- 1 | // 2 | // Stamps the version into the package.json file. 3 | // 4 | 5 | const fs = require("fs"); 6 | const package = JSON.parse(fs.readFileSync("package.json", "utf8")); 7 | package.version = process.env.VERSION; 8 | fs.writeFileSync("package.json", JSON.stringify(package, null, 4)); -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": { 8 | "object-literal-shorthand": false, 9 | "max-line-length": false, 10 | "no-trailing-whitespace": false, 11 | "no-console": false, 12 | "eofline": false, 13 | "object-literal-sort-keys": false, 14 | "one-line": [ 15 | true, 16 | "check-catch", 17 | "check-finally", 18 | "check-open-brace", 19 | "check-whitespace" 20 | ], 21 | "ordered-imports": false, 22 | "arrow-parens": false, 23 | "comment-format": false, 24 | "no-var-requires": false, 25 | "trailing-comma": [ 26 | true, 27 | { 28 | "multiline": { 29 | "objects": "always", 30 | "arrays": "always", 31 | "functions": "never", 32 | "typeLiterals": "ignore" 33 | }, 34 | "esSpecCompliant": true 35 | } 36 | ] 37 | }, 38 | "rulesDirectory": [] 39 | } --------------------------------------------------------------------------------