Repository: algorand/js-algorand-sdk
Pull request #737 (2023-01-27 00:20:15)

FOR REVIEW ONLY - Release v2.1.0
<!-- Release notes generated using configuration in .github/release.yml at develop -->

## What's Changed
### Enhancements
* Enhancement: Add foreign array objects to ATC `addMethodCall` by @algochoi in https://github.com/algorand/js-algorand-sdk/pull/725
* API: verifyTealSign utility function by @M-Picco in https://github.com/algorand/js-algorand-sdk/pull/735
* Packaging: Don't use global `Buffer` object by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/733
* algod REST API: Add support for algod /v2/teal/disassemble by @michaeldiamant in https://github.com/algorand/js-algorand-sdk/pull/702

## New Contributors
* @M-Picco made their first contribution in https://github.com/algorand/js-algorand-sdk/pull/735

**Full Changelog**: https://github.com/algorand/js-algorand-sdk/compare/v2.0.0...v2.1.0


Patch:
diff --git a/.eslintrc.js b/.eslintrc.js
index 78f7990..fcd70fb 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -1,17 +1,31 @@
 module.exports = {
   env: {
-    browser: true,
     es2021: true,
-    node: true,
-    mocha: true,
+    'shared-node-browser': true,
   },
-  extends: ['airbnb-base', 'prettier', 'plugin:import/typescript'],
+  extends: [
+    'airbnb-base',
+    'prettier',
+    'plugin:import/typescript',
+    'eslint:recommended',
+  ],
   parser: '@typescript-eslint/parser',
   parserOptions: {
     ecmaVersion: 12,
   },
   plugins: ['@typescript-eslint', 'eslint-plugin-tsdoc'],
   rules: {
+    'no-restricted-globals': [
+      'error',
+      {
+        // This is to ensure that we use the 'buffer' package in the browser. In Node it doesn't
+        // make a difference.
+        name: 'Buffer',
+        message:
+          "Explictly import Buffer with `import { Buffer } from 'buffer'`",
+      },
+    ],
+    'no-constant-condition': ['error', { checkLoops: false }],
     'no-restricted-syntax': ['error', 'LabeledStatement', 'WithStatement'],
     'no-plusplus': ['error', { allowForLoopAfterthoughts: true }],
     'max-classes-per-file': 'off',
diff --git a/.gitignore b/.gitignore
index 47d5bc3..6bf3410 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,8 @@
 .DS_Store
 
 .idea/
+# Webstorm
+*.iml
 .vscode/*
 !.vscode/settings.json
 !.vscode/extensions.json
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f47b23b..8c71151 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,21 @@
-# 2.0.0
+# v2.1.0
+
+## What's Changed
+
+### Enhancements
+
+- Enhancement: Add foreign array objects to ATC `addMethodCall` by @algochoi in https://github.com/algorand/js-algorand-sdk/pull/725
+- API: verifyTealSign utility function by @M-Picco in https://github.com/algorand/js-algorand-sdk/pull/735
+- Packaging: Don't use global `Buffer` object by @jasonpaulos in https://github.com/algorand/js-algorand-sdk/pull/733
+- algod REST API: Add support for algod /v2/teal/disassemble by @michaeldiamant in https://github.com/algorand/js-algorand-sdk/pull/702
+
+## New Contributors
+
+- @M-Picco made their first contribution in https://github.com/algorand/js-algorand-sdk/pull/735
+
+**Full Changelog**: https://github.com/algorand/js-algorand-sdk/compare/v2.0.0...v2.1.0
+
+# v2.0.0
 
 ## What's Changed
 
diff --git a/README.md b/README.md
index afbf7c2..cc1f9ba 100644
--- a/README.md
+++ b/README.md
@@ -22,8 +22,8 @@ Include a minified browser bundle directly in your HTML like so:
 
 ```html
 <script
-  src="https://unpkg.com/algosdk@v2.0.0/dist/browser/algosdk.min.js"
-  integrity="sha384-NBHc5NXk7w78aVp9uPMo1JmBan6l4MHxzhiKXgYT8wtBoeIMHEuu5OECfGHYTLh7"
+  src="https://unpkg.com/algosdk@v2.1.0/dist/browser/algosdk.min.js"
+  integrity="sha384-SwM9TdxjIFpIT2FUfchrQvtwi748UI0q12Zi32yd6BM8D/lCTumPhw0+BrPC0yd0"
   crossorigin="anonymous"
 ></script>
 ```
@@ -32,8 +32,8 @@ or
 
 ```html
 <script
-  src="https://cdn.jsdelivr.net/npm/algosdk@v2.0.0/dist/browser/algosdk.min.js"
-  integrity="sha384-NBHc5NXk7w78aVp9uPMo1JmBan6l4MHxzhiKXgYT8wtBoeIMHEuu5OECfGHYTLh7"
+  src="https://cdn.jsdelivr.net/npm/algosdk@v2.1.0/dist/browser/algosdk.min.js"
+  integrity="sha384-SwM9TdxjIFpIT2FUfchrQvtwi748UI0q12Zi32yd6BM8D/lCTumPhw0+BrPC0yd0"
   crossorigin="anonymous"
 ></script>
 ```
diff --git a/examples/app_transaction_examples.js b/examples/app_transaction_examples.js
index b4d3f3f..14529b7 100644
--- a/examples/app_transaction_examples.js
+++ b/examples/app_transaction_examples.js
@@ -5,6 +5,7 @@
 // makeApplicationCreateTxnFromObject, makeApplicationOptInTxnFromObject, etc.
 // counterparts in your code for readability.
 
+const { Buffer } = require('buffer');
 const algosdk = require('../src');
 const utils = require('./utils');
 
diff --git a/examples/asset_create_example.js b/examples/asset_create_example.js
index ee53872..1bb8eb5 100644
--- a/examples/asset_create_example.js
+++ b/examples/asset_create_example.js
@@ -1,5 +1,6 @@
 // Example: creating an asset
 
+const { Buffer } = require('buffer');
 const algosdk = require('../src');
 const utils = require('./utils');
 
diff --git a/examples/logic_sig_example.js b/examples/logic_sig_example.js
index 36b1370..11540f9 100644
--- a/examples/logic_sig_example.js
+++ b/examples/logic_sig_example.js
@@ -1,5 +1,6 @@
 // Example: creating a LogicSig transaction signed by a program that never approves the transfer.
 
+const { Buffer } = require('buffer');
 const algosdk = require('../src');
 const utils = require('./utils');
 
diff --git a/examples/notefield_example.js b/examples/notefield_example.js
index 2754db5..067ce2a 100644
--- a/examples/notefield_example.js
+++ b/examples/notefield_example.js
@@ -5,6 +5,7 @@
  * want in the "note" field.
  */
 
+const { Buffer } = require('buffer');
 const algosdk = require('../src');
 const utils = require('./utils');
 
diff --git a/examples/typescript_example.ts b/examples/typescript_example.ts
index f0e298c..9dff30b 100644
--- a/examples/typescript_example.ts
+++ b/examples/typescript_example.ts
@@ -1,5 +1,6 @@
 // Example: with TypeScript
 
+import { Buffer } from 'buffer';
 import algosdk from '../src';
 import utils from './utils';
 
diff --git a/package-lock.json b/package-lock.json
index 2d937dc..f295d53 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,16 +1,16 @@
 {
   "name": "algosdk",
-  "version": "2.0.0",
+  "version": "2.1.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "algosdk",
-      "version": "2.0.0",
+      "version": "2.1.0",
       "license": "MIT",
       "dependencies": {
         "algo-msgpack-with-bigint": "^2.1.1",
-        "buffer": "^6.0.2",
+        "buffer": "^6.0.3",
         "cross-fetch": "^3.1.5",
         "hi-base32": "^0.5.1",
         "js-sha256": "^0.9.0",
@@ -23,7 +23,6 @@
       "devDependencies": {
         "@types/json-bigint": "^1.0.0",
         "@types/mocha": "^8.2.2",
-        "@types/url-parse": "^1.4.3",
         "@typescript-eslint/eslint-plugin": "^4.26.1",
         "@typescript-eslint/parser": "^4.26.1",
         "assert": "^2.0.0",
@@ -455,12 +454,6 @@
         "@types/node": "*"
       }
     },
-    "node_modules/@types/url-parse": {
-      "version": "1.4.3",
-      "resolved": "https://registry.npmjs.org/@types/url-parse/-/url-parse-1.4.3.tgz",
-      "integrity": "sha512-4kHAkbV/OfW2kb5BLVUuUMoumB3CP8rHqlw48aHvFy5tf9ER0AfOonBlX29l/DD68G70DmyhRlSYfQPSYpC5Vw==",
-      "dev": true
-    },
     "node_modules/@types/yauzl": {
       "version": "2.9.1",
       "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz",
@@ -8526,12 +8519,6 @@
         "@types/node": "*"
       }
     },
-    "@types/url-parse": {
-      "version": "1.4.3",
-      "resolved": "https://registry.npmjs.org/@types/url-parse/-/url-parse-1.4.3.tgz",
-      "integrity": "sha512-4kHAkbV/OfW2kb5BLVUuUMoumB3CP8rHqlw48aHvFy5tf9ER0AfOonBlX29l/DD68G70DmyhRlSYfQPSYpC5Vw==",
-      "dev": true
-    },
     "@types/yauzl": {
       "version": "2.9.1",
       "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz",
diff --git a/package.json b/package.json
index 2db1054..61e2aeb 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "algosdk",
-  "version": "2.0.0",
+  "version": "2.1.0",
   "description": "The official JavaScript SDK for Algorand",
   "main": "dist/cjs/index.js",
   "module": "dist/esm/index.js",
@@ -22,7 +22,7 @@
   },
   "dependencies": {
     "algo-msgpack-with-bigint": "^2.1.1",
-    "buffer": "^6.0.2",
+    "buffer": "^6.0.3",
     "cross-fetch": "^3.1.5",
     "hi-base32": "^0.5.1",
     "js-sha256": "^0.9.0",
@@ -35,7 +35,6 @@
   "devDependencies": {
     "@types/json-bigint": "^1.0.0",
     "@types/mocha": "^8.2.2",
-    "@types/url-parse": "^1.4.3",
     "@typescript-eslint/eslint-plugin": "^4.26.1",
     "@typescript-eslint/parser": "^4.26.1",
     "assert": "^2.0.0",
diff --git a/src/abi/abi_type.ts b/src/abi/abi_type.ts
index 3ea89e6..cbcbd9b 100644
--- a/src/abi/abi_type.ts
+++ b/src/abi/abi_type.ts
@@ -13,6 +13,7 @@
     // | string
     // | (T1, ..., Tn)
 */
+import { Buffer } from 'buffer';
 import { encodeAddress, decodeAddress } from '../encoding/address';
 import { bigIntToBytes, bytesToBigInt } from '../encoding/bigint';
 import { concatArrays } from '../utils/utils';
diff --git a/src/bid.ts b/src/bid.ts
index 8d4485e..7874d64 100644
--- a/src/bid.ts
+++ b/src/bid.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import * as address from './encoding/address';
 import * as encoding from './encoding/encoding';
 import * as nacl from './nacl/naclWrappers';
diff --git a/src/client/client.ts b/src/client/client.ts
index b40a0a6..6a20366 100644
--- a/src/client/client.ts
+++ b/src/client/client.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import * as utils from '../utils/utils';
 import {
   BaseHTTPClient,
diff --git a/src/client/kmd.ts b/src/client/kmd.ts
index 3a1bd27..4c832b2 100644
--- a/src/client/kmd.ts
+++ b/src/client/kmd.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import ServiceClient from './v2/serviceClient';
 import * as txn from '../transaction';
 import { CustomTokenHeader, KMDTokenHeader } from './urlTokenBaseHTTPClient';
diff --git a/src/client/urlTokenBaseHTTPClient.ts b/src/client/urlTokenBaseHTTPClient.ts
index 85c20d5..aba61fe 100644
--- a/src/client/urlTokenBaseHTTPClient.ts
+++ b/src/client/urlTokenBaseHTTPClient.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import { fetch, Response, Headers } from 'cross-fetch';
 import {
   BaseHTTPClient,
diff --git a/src/client/v2/algod/algod.ts b/src/client/v2/algod/algod.ts
index ce19602..ad309e3 100644
--- a/src/client/v2/algod/algod.ts
+++ b/src/client/v2/algod/algod.ts
@@ -30,6 +30,7 @@ import {
 } from '../../urlTokenBaseHTTPClient';
 import LightBlockHeaderProof from './lightBlockHeaderProof';
 import StateProof from './stateproof';
+import Disassemble from './disassemble';
 
 /**
  * Algod client connects an application to the Algorand blockchain. The algod client requires a valid algod REST endpoint IP address and algod token from an Algorand node that is connected to the network you plan to interact with.
@@ -397,6 +398,22 @@ export default class AlgodClient extends ServiceClient {
     return new Compile(this.c, source);
   }
 
+  /**
+   * Given the program bytes, return the TEAL source code in plain text.
+   *
+   * #### Example
+   * ```typescript
+   * const bytecode = "TEAL bytecode";
+   * const disassembledSource = await algodClient.disassemble(bytecode).do();
+   * ```
+   *
+   * @remarks This endpoint is only enabled when a node's configuration file sets EnableDeveloperAPI to true.
+   * @param source
+   */
+  disassemble(source: string | Uint8Array) {
+    return new Disassemble(this.c, source);
+  }
+
   /**
    * Provides debugging information for a transaction (or group).
    *
diff --git a/src/client/v2/algod/compile.ts b/src/client/v2/algod/compile.ts
index 85f02f1..db8769c 100644
--- a/src/client/v2/algod/compile.ts
+++ b/src/client/v2/algod/compile.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import JSONRequest from '../jsonrequest';
 import HTTPClient from '../../client';
 
diff --git a/src/client/v2/algod/disassemble.ts b/src/client/v2/algod/disassemble.ts
new file mode 100644
index 0000000..552e8c6
--- /dev/null
+++ b/src/client/v2/algod/disassemble.ts
@@ -0,0 +1,46 @@
+import { Buffer } from 'buffer';
+import JSONRequest from '../jsonrequest';
+import HTTPClient from '../../client';
+
+/**
+ * Sets the default header (if not previously set)
+ * @param headers - A headers object
+ */
+export function setHeaders(headers = {}) {
+  let hdrs = headers;
+  if (Object.keys(hdrs).every((key) => key.toLowerCase() !== 'content-type')) {
+    hdrs = { ...headers };
+    hdrs['Content-Type'] = 'text/plain';
+  }
+  return hdrs;
+}
+
+/**
+ * Executes disassemble
+ */
+export default class Disassemble extends JSONRequest {
+  constructor(c: HTTPClient, private source: string | Uint8Array) {
+    super(c);
+    this.source = source;
+  }
+
+  // eslint-disable-next-line class-methods-use-this
+  path() {
+    return `/v2/teal/disassemble`;
+  }
+
+  /**
+   * Executes disassemble
+   * @param headers - A headers object
+   */
+  async do(headers = {}) {
+    const txHeaders = setHeaders(headers);
+    const res = await this.c.post(
+      this.path(),
+      Buffer.from(this.source),
+      txHeaders,
+      this.query
+    );
+    return res.body;
+  }
+}
diff --git a/src/client/v2/algod/dryrun.ts b/src/client/v2/algod/dryrun.ts
index 7e5b650..98b2fb4 100644
--- a/src/client/v2/algod/dryrun.ts
+++ b/src/client/v2/algod/dryrun.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import JSONRequest from '../jsonrequest';
 import HTTPClient from '../../client';
 import * as modelsv2 from './models/types';
diff --git a/src/client/v2/algod/getApplicationBoxByName.ts b/src/client/v2/algod/getApplicationBoxByName.ts
index 29e8323..789fb0b 100644
--- a/src/client/v2/algod/getApplicationBoxByName.ts
+++ b/src/client/v2/algod/getApplicationBoxByName.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import JSONRequest from '../jsonrequest';
 import HTTPClient from '../../client';
 import IntDecoding from '../../../types/intDecoding';
diff --git a/src/client/v2/algod/models/types.ts b/src/client/v2/algod/models/types.ts
index a8f6303..4d13d8d 100644
--- a/src/client/v2/algod/models/types.ts
+++ b/src/client/v2/algod/models/types.ts
@@ -3,6 +3,7 @@
  */
 
 /* eslint-disable no-use-before-define */
+import { Buffer } from 'buffer';
 import BaseModel from '../../basemodel';
 import { EncodedSignedTransaction } from '../../../../types/transactions/encoded';
 import BlockHeader from '../../../../types/blockHeader';
diff --git a/src/client/v2/algod/sendRawTransaction.ts b/src/client/v2/algod/sendRawTransaction.ts
index e34e68c..8977c1c 100644
--- a/src/client/v2/algod/sendRawTransaction.ts
+++ b/src/client/v2/algod/sendRawTransaction.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import JSONRequest from '../jsonrequest';
 import HTTPClient from '../../client';
 import { concatArrays } from '../../../utils/utils';
diff --git a/src/client/v2/basemodel.ts b/src/client/v2/basemodel.ts
index ffb0ea1..39fa52e 100644
--- a/src/client/v2/basemodel.ts
+++ b/src/client/v2/basemodel.ts
@@ -1,3 +1,5 @@
+import { Buffer } from 'buffer';
+
 /**
  * Base class for models
  */
diff --git a/src/client/v2/indexer/lookupAccountTransactions.ts b/src/client/v2/indexer/lookupAccountTransactions.ts
index b2343f1..12f48fa 100644
--- a/src/client/v2/indexer/lookupAccountTransactions.ts
+++ b/src/client/v2/indexer/lookupAccountTransactions.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import JSONRequest from '../jsonrequest';
 import HTTPClient from '../../client';
 import IntDecoding from '../../../types/intDecoding';
diff --git a/src/client/v2/indexer/lookupApplicationBoxByIDandName.ts b/src/client/v2/indexer/lookupApplicationBoxByIDandName.ts
index 68b456a..94bc801 100644
--- a/src/client/v2/indexer/lookupApplicationBoxByIDandName.ts
+++ b/src/client/v2/indexer/lookupApplicationBoxByIDandName.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import JSONRequest from '../jsonrequest';
 import HTTPClient from '../../client';
 import IntDecoding from '../../../types/intDecoding';
diff --git a/src/client/v2/indexer/models/types.ts b/src/client/v2/indexer/models/types.ts
index 7f5e417..12b0555 100644
--- a/src/client/v2/indexer/models/types.ts
+++ b/src/client/v2/indexer/models/types.ts
@@ -3,6 +3,7 @@
  */
 
 /* eslint-disable no-use-before-define */
+import { Buffer } from 'buffer';
 import BaseModel from '../../basemodel';
 
 /**
diff --git a/src/composer.ts b/src/composer.ts
index b9756b6..7fba1d3 100644
--- a/src/composer.ts
+++ b/src/composer.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import {
   ABIType,
   ABITupleType,
@@ -202,6 +203,9 @@ export class AtomicTransactionComposer {
     numLocalInts,
     numLocalByteSlices,
     extraPages,
+    appAccounts,
+    appForeignApps,
+    appForeignAssets,
     boxes,
     note,
     lease,
@@ -234,6 +238,12 @@ export class AtomicTransactionComposer {
     numLocalByteSlices?: number;
     /** The number of extra pages to allocate for the application's programs. Only set this if this is an application creation call. If omitted, defaults to 0. */
     extraPages?: number;
+    /** Array of Address strings that represent external accounts supplied to this application. If accounts are provided here, the accounts specified in the method args will appear after these. */
+    appAccounts?: string[];
+    /** Array of App ID numbers that represent external apps supplied to this application. If apps are provided here, the apps specified in the method args will appear after these. */
+    appForeignApps?: number[];
+    /** Array of Asset ID numbers that represent external assets supplied to this application. If assets are provided here, the assets specified in the method args will appear after these. */
+    appForeignAssets?: number[];
     /** The box references for this application call */
     boxes?: BoxReference[];
     /** The note value for this application call */
@@ -370,9 +380,12 @@ export class AtomicTransactionComposer {
     }
 
     const resolvedRefIndexes: number[] = [];
-    const foreignAccounts: string[] = [];
-    const foreignApps: number[] = [];
-    const foreignAssets: number[] = [];
+    const foreignAccounts: string[] =
+      appAccounts == null ? [] : appAccounts.slice();
+    const foreignApps: number[] =
+      appForeignApps == null ? [] : appForeignApps.slice();
+    const foreignAssets: number[] =
+      appForeignAssets == null ? [] : appForeignAssets.slice();
     for (let i = 0; i < refArgTypes.length; i++) {
       const refType = refArgTypes[i];
       const refValue = refArgValues[i];
diff --git a/src/dryrun.ts b/src/dryrun.ts
index 5755e74..ce9739b 100644
--- a/src/dryrun.ts
+++ b/src/dryrun.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import AlgodClient from './client/v2/algod/algod';
 import {
   AccountStateDelta,
diff --git a/src/encoding/address.ts b/src/encoding/address.ts
index dcd445f..b1e287e 100644
--- a/src/encoding/address.ts
+++ b/src/encoding/address.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import base32 from 'hi-base32';
 import * as nacl from '../nacl/naclWrappers';
 import * as utils from '../utils/utils';
diff --git a/src/encoding/bigint.ts b/src/encoding/bigint.ts
index 1f1b15d..cc03990 100644
--- a/src/encoding/bigint.ts
+++ b/src/encoding/bigint.ts
@@ -1,3 +1,5 @@
+import { Buffer } from 'buffer';
+
 /**
  * bigIntToBytes converts a BigInt to a big-endian Uint8Array for encoding.
  * @param bi - The bigint to convert.
diff --git a/src/encoding/uint64.ts b/src/encoding/uint64.ts
index 3dc6e1a..d190996 100644
--- a/src/encoding/uint64.ts
+++ b/src/encoding/uint64.ts
@@ -1,3 +1,10 @@
+import { concatArrays } from '../utils/utils';
+
+// NOTE: at the moment we specifically do not use Buffer.writeBigUInt64BE and
+// Buffer.readBigUInt64BE. This is because projects using webpack v4
+// automatically include an old version of the npm `buffer` package (v4.9.2 at
+// the time of writing), and this old version does not have these methods.
+
 /**
  * encodeUint64 converts an integer to its binary representation.
  * @param num - The number to convert. This must be an unsigned integer less than
@@ -12,11 +19,11 @@ export function encodeUint64(num: number | bigint) {
     throw new Error('Input is not a 64-bit unsigned integer');
   }
 
-  const buf = Buffer.allocUnsafe(8);
+  const encoding = new Uint8Array(8);
+  const view = new DataView(encoding.buffer);
+  view.setBigUint64(0, BigInt(num));
 
-  buf.writeBigUInt64BE(BigInt(num));
-
-  return new Uint8Array(buf);
+  return encoding;
 }
 
 /**
@@ -59,13 +66,12 @@ export function decodeUint64(data: any, decodingMode: any = 'safe') {
   }
 
   // insert 0s at the beginning if data is smaller than 8 bytes
-  const padding = Buffer.allocUnsafe(8 - data.byteLength);
-  padding.fill(0);
-
-  const buf = Buffer.concat([padding, Buffer.from(data)]);
+  const padding = new Uint8Array(8 - data.byteLength);
+  const encoding = concatArrays(padding, data);
+  const view = new DataView(encoding.buffer);
 
-  const num = buf.readBigUInt64BE();
-  const isBig = num > Number.MAX_SAFE_INTEGER;
+  const num = view.getBigUint64(0);
+  const isBig = num > BigInt(Number.MAX_SAFE_INTEGER);
 
   if (decodingMode === 'safe') {
     if (isBig) {
diff --git a/src/group.ts b/src/group.ts
index c20d5be..0e26384 100644
--- a/src/group.ts
+++ b/src/group.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import * as txnBuilder from './transaction';
 import * as nacl from './nacl/naclWrappers';
 import * as encoding from './encoding/encoding';
diff --git a/src/logicsig.ts b/src/logicsig.ts
index c63e6af..34a35ca 100644
--- a/src/logicsig.ts
+++ b/src/logicsig.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import * as nacl from './nacl/naclWrappers';
 import * as address from './encoding/address';
 import * as encoding from './encoding/encoding';
@@ -495,6 +496,29 @@ export function tealSign(
   return nacl.sign(toBeSigned, sk);
 }
 
+/**
+ * verifyTealSign verifies a signature as would the ed25519verify opcode
+ * @param data - buffer with original signed data
+ * @param programHash - string representation of teal program hash (= contract address for LogicSigs)
+ * @param sig - uint8array with the signature to verify (produced by tealSign/tealSignFromProgram)
+ * @param pk - uint8array with public key to verify against
+ */
+export function verifyTealSign(
+  data: Uint8Array | Buffer,
+  programHash: string,
+  sig: Uint8Array,
+  pk: Uint8Array
+) {
+  const parts = utils.concatArrays(
+    address.decodeAddress(programHash).publicKey,
+    data
+  );
+  const toBeSigned = Buffer.from(
+    utils.concatArrays(SIGN_PROGRAM_DATA_PREFIX, parts)
+  );
+  return nacl.verify(toBeSigned, sig, pk);
+}
+
 /**
  * tealSignFromProgram creates a signature compatible with ed25519verify opcode from raw program bytes
  * @param sk - uint8array with secret key
diff --git a/src/main.ts b/src/main.ts
index 2fa4d21..e15ecc9 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import * as nacl from './nacl/naclWrappers';
 import * as address from './encoding/address';
 import * as encoding from './encoding/encoding';
@@ -160,6 +161,7 @@ export {
   logicSigFromByte,
   tealSign,
   tealSignFromProgram,
+  verifyTealSign,
 } from './logicsig';
 export {
   signMultisigTransaction,
diff --git a/src/multisig.ts b/src/multisig.ts
index 8efd579..7bf821d 100644
--- a/src/multisig.ts
+++ b/src/multisig.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import * as nacl from './nacl/naclWrappers';
 import * as address from './encoding/address';
 import * as encoding from './encoding/encoding';
diff --git a/src/transaction.ts b/src/transaction.ts
index a74778c..c91fdc4 100644
--- a/src/transaction.ts
+++ b/src/transaction.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import base32 from 'hi-base32';
 import * as address from './encoding/address';
 import * as encoding from './encoding/encoding';
diff --git a/src/types/transactions/encoded.ts b/src/types/transactions/encoded.ts
index 2f0f48e..7864e4e 100644
--- a/src/types/transactions/encoded.ts
+++ b/src/types/transactions/encoded.ts
@@ -1,3 +1,5 @@
+import { Buffer } from 'buffer';
+
 /**
  * Interfaces for the encoded transaction object. Every property is labelled with its associated Transaction type property
  */
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
index c504a09..6abfbc0 100644
--- a/src/utils/utils.ts
+++ b/src/utils/utils.ts
@@ -114,8 +114,11 @@ export function removeUndefinedProperties(
  */
 export function isNode() {
   return (
+    // @ts-ignore
     typeof process === 'object' &&
+    // @ts-ignore
     typeof process.versions === 'object' &&
+    // @ts-ignore
     typeof process.versions.node !== 'undefined'
   );
 }
diff --git a/webpack.config.js b/webpack.config.js
index e1c6728..74f8bc7 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,5 +1,4 @@
 const path = require('path');
-const webpack = require('webpack');
 
 module.exports = {
   mode: 'production',
@@ -17,11 +16,6 @@ module.exports = {
     // Add '.ts' as resolvable extensions
     extensions: ['.ts', '.js'],
   },
-  plugins: [
-    new webpack.ProvidePlugin({
-      Buffer: ['buffer', 'Buffer'],
-    }),
-  ],
   module: {
     rules: [
       // All files with a '.ts' extension will be handled by 'ts-loader'.


Test patch:
diff --git a/tests/1.Mnemonics_test.js b/tests/1.Mnemonics_test.js
index 9e388e7..de8b183 100644
--- a/tests/1.Mnemonics_test.js
+++ b/tests/1.Mnemonics_test.js
@@ -1,3 +1,4 @@
+/* eslint-env mocha */
 const assert = require('assert');
 const algosdk = require('../src/index');
 const nacl = require('../src/nacl/naclWrappers');
diff --git a/tests/10.ABI.ts b/tests/10.ABI.ts
index 68447aa..d8a8600 100644
--- a/tests/10.ABI.ts
+++ b/tests/10.ABI.ts
@@ -1,3 +1,4 @@
+/* eslint-env mocha */
 import assert from 'assert';
 import {
   ABIAddressType,
@@ -12,6 +13,13 @@ import {
   ABIType,
   ABIValue,
 } from '../src/abi/abi_type';
+import {
+  AtomicTransactionComposer,
+  ABIMethod,
+  makeBasicAccountTransactionSigner,
+  generateAccount,
+  AtomicTransactionComposerStatus,
+} from '../src';
 import { decodeAddress } from '../src/encoding/address';
 
 describe('ABI type checking', () => {
@@ -474,4 +482,56 @@ describe('ABI encoding', () => {
       ])
     );
   });
+
+  it('should properly accept foreign array objects in the ATC addMethodCall', () => {
+    const composer = new AtomicTransactionComposer();
+    const method = ABIMethod.fromSignature('add(application)uint8');
+    const account = generateAccount();
+    const sender = 'DN7MBMCL5JQ3PFUQS7TMX5AH4EEKOBJVDUF4TCV6WERATKFLQF4MQUPZTA';
+    const sp = {
+      fee: 1000,
+      firstRound: 1,
+      lastRound: 1001,
+      genesisID: 'gi',
+      genesisHash: 'gh',
+    };
+    const foreignAcct =
+      'E4VCHISDQPLIZWMALIGNPK2B2TERPDMR64MZJXE3UL75MUDXZMADX5OWXM';
+
+    // Create method call using ATC.
+    // The foreign apps array argument should be packed before the method argument.
+    composer.addMethodCall({
+      appID: 7,
+      method,
+      sender,
+      suggestedParams: sp,
+      methodArgs: [2],
+      appAccounts: [foreignAcct],
+      appForeignApps: [1],
+      appForeignAssets: [124],
+      signer: makeBasicAccountTransactionSigner(account),
+    });
+
+    assert.deepStrictEqual(
+      composer.getStatus(),
+      AtomicTransactionComposerStatus.BUILDING
+    );
+    assert.deepStrictEqual(composer.count(), 1);
+
+    // The built group should have one txn.
+    const txns = composer.buildGroup();
+    // eslint-disable-next-line prefer-destructuring
+    const txn = txns[0].txn;
+
+    // Assert that foreign objects were passed in and ordering was correct.
+    assert.deepStrictEqual(txn.appForeignApps?.length, 2);
+    assert.deepStrictEqual(txn.appForeignApps[0], 1);
+    assert.deepStrictEqual(txn.appForeignApps[1], 2);
+
+    assert.deepStrictEqual(txn.appForeignAssets?.length, 1);
+    assert.deepStrictEqual(txn.appForeignAssets[0], 124);
+
+    assert.deepStrictEqual(txn.appAccounts?.length, 1);
+    assert.deepStrictEqual(txn.appAccounts[0], decodeAddress(foreignAcct));
+  });
 });
diff --git a/tests/2.Encoding.js b/tests/2.Encoding.js
index 4961c96..b3f6ca0 100644
--- a/tests/2.Encoding.js
+++ b/tests/2.Encoding.js
@@ -1,3 +1,5 @@
+/* eslint-env mocha */
+const { Buffer } = require('buffer');
 const assert = require('assert');
 const algosdk = require('../src/index');
 const utils = require('../src/utils/utils');
diff --git a/tests/3.Address.js b/tests/3.Address.js
index 78090f8..73ac2b2 100644
--- a/tests/3.Address.js
+++ b/tests/3.Address.js
@@ -1,3 +1,4 @@
+/* eslint-env mocha */
 const assert = require('assert');
 const nacl = require('../src/nacl/naclWrappers');
 const algosdk = require('../src/index');
diff --git a/tests/4.Utils.ts b/tests/4.Utils.ts
index c372fed..a2b589d 100644
--- a/tests/4.Utils.ts
+++ b/tests/4.Utils.ts
@@ -1,3 +1,4 @@
+/* eslint-env mocha */
 import assert from 'assert';
 import * as utils from '../src/utils/utils';
 import * as nacl from '../src/nacl/naclWrappers';
diff --git a/tests/5.Transaction.js b/tests/5.Transaction.js
index 0b2d3b3..7069d4b 100644
--- a/tests/5.Transaction.js
+++ b/tests/5.Transaction.js
@@ -1,3 +1,5 @@
+/* eslint-env mocha */
+const { Buffer } = require('buffer');
 const assert = require('assert');
 const algosdk = require('../src/index');
 const { translateBoxReferences } = require('../src/boxStorage');
diff --git a/tests/6.Multisig.ts b/tests/6.Multisig.ts
index b2030ce..a2577e9 100644
--- a/tests/6.Multisig.ts
+++ b/tests/6.Multisig.ts
@@ -1,3 +1,5 @@
+/* eslint-env mocha */
+import { Buffer } from 'buffer';
 import assert from 'assert';
 import algosdk from '../src/index';
 import {
diff --git a/tests/7.AlgoSDK.js b/tests/7.AlgoSDK.js
index 94686e9..e54a597 100644
--- a/tests/7.AlgoSDK.js
+++ b/tests/7.AlgoSDK.js
@@ -1,3 +1,5 @@
+/* eslint-env mocha */
+const { Buffer } = require('buffer');
 const assert = require('assert');
 const algosdk = require('../src/index');
 const nacl = require('../src/nacl/naclWrappers');
@@ -967,22 +969,18 @@ describe('Algosdk (AKA end to end)', () => {
   });
 
   describe('tealSign', () => {
-    it('should produce verifiable signature', () => {
-      const data = Buffer.from('Ux8jntyBJQarjKGF8A==', 'base64');
-      const seed = Buffer.from(
-        '5Pf7eGMA52qfMT4R4/vYCt7con/7U3yejkdXkrcb26Q=',
-        'base64'
-      );
-      const prog = Buffer.from('ASABASI=', 'base64');
+    const data = Buffer.from('Ux8jntyBJQarjKGF8A==', 'base64');
+    const prog = Buffer.from('ASABASI=', 'base64');
+    const addr = new algosdk.LogicSig(prog).address();
 
-      const keys = nacl.keyPairFromSeed(seed);
-      const pk = keys.publicKey;
-      const sk = keys.secretKey;
-      const addr = new algosdk.LogicSig(prog).address();
-      const sig1 = algosdk.tealSign(sk, data, addr);
-      const sig2 = algosdk.tealSignFromProgram(sk, data, prog);
+    const seed = Buffer.from(
+      '5Pf7eGMA52qfMT4R4/vYCt7con/7U3yejkdXkrcb26Q=',
+      'base64'
+    );
+    const { publicKey: pk, secretKey: sk } = nacl.keyPairFromSeed(seed);
 
-      assert.deepStrictEqual(sig1, sig2);
+    it('should produce a verifiable signature', () => {
+      const sig = algosdk.tealSign(sk, data, addr);
 
       const parts = utils.concatArrays(
         algosdk.decodeAddress(addr).publicKey,
@@ -991,7 +989,21 @@ describe('Algosdk (AKA end to end)', () => {
       const toBeVerified = Buffer.from(
         utils.concatArrays(Buffer.from('ProgData'), parts)
       );
-      const verified = nacl.verify(toBeVerified, sig1, pk);
+      const verified = nacl.verify(toBeVerified, sig, pk);
+      assert.equal(verified, true);
+    });
+
+    it('should produce a verifiable signature from a program', () => {
+      const sig1 = algosdk.tealSign(sk, data, addr);
+      const sig2 = algosdk.tealSignFromProgram(sk, data, prog);
+
+      assert.deepStrictEqual(sig1, sig2);
+    });
+
+    it('should verify a valid signature', () => {
+      const sig = algosdk.tealSign(sk, data, addr);
+
+      const verified = algosdk.verifyTealSign(data, addr, sig, pk);
       assert.equal(verified, true);
     });
   });
diff --git a/tests/8.LogicSig.ts b/tests/8.LogicSig.ts
index 13609ac..f9c38a3 100644
--- a/tests/8.LogicSig.ts
+++ b/tests/8.LogicSig.ts
@@ -1,3 +1,5 @@
+/* eslint-env mocha */
+import { Buffer } from 'buffer';
 import assert from 'assert';
 import algosdk from '../src/index';
 
diff --git a/tests/9.Client.ts b/tests/9.Client.ts
index f662cef..b10c842 100644
--- a/tests/9.Client.ts
+++ b/tests/9.Client.ts
@@ -1,3 +1,4 @@
+/* eslint-env mocha */
 import assert from 'assert';
 import HTTPClient from '../src/client/client';
 import { URLTokenBaseHTTPClient } from '../src/client/urlTokenBaseHTTPClient';
diff --git a/tests/cucumber/browser/test.js b/tests/cucumber/browser/test.js
index 7fd997d..e276efc 100644
--- a/tests/cucumber/browser/test.js
+++ b/tests/cucumber/browser/test.js
@@ -1,3 +1,5 @@
+/* eslint-env browser */
+const { Buffer } = require('buffer');
 const assert = require('assert');
 const sha512 = require('js-sha512');
 const nacl = require('tweetnacl');
diff --git a/tests/cucumber/browser/webpack.config.js b/tests/cucumber/browser/webpack.config.js
index f0a59ef..d39c543 100644
--- a/tests/cucumber/browser/webpack.config.js
+++ b/tests/cucumber/browser/webpack.config.js
@@ -1,5 +1,4 @@
 const path = require('path');
-const webpack = require('webpack');
 
 module.exports = {
   entry: path.resolve(__dirname, 'test.js'),
@@ -8,9 +7,4 @@ module.exports = {
     path: path.resolve(__dirname, 'build'),
   },
   devtool: 'source-map',
-  plugins: [
-    new webpack.ProvidePlugin({
-      Buffer: ['buffer', 'Buffer'],
-    }),
-  ],
 };
diff --git a/tests/cucumber/integration.tags b/tests/cucumber/integration.tags
index 91dace7..f8b49f9 100644
--- a/tests/cucumber/integration.tags
+++ b/tests/cucumber/integration.tags
@@ -6,6 +6,7 @@
 @auction
 @c2c
 @compile
+@compile.disassemble
 @compile.sourcemap
 @dryrun
 @kmd
diff --git a/tests/cucumber/steps/index.js b/tests/cucumber/steps/index.js
index 0117dc8..2b411ff 100644
--- a/tests/cucumber/steps/index.js
+++ b/tests/cucumber/steps/index.js
@@ -1,4 +1,5 @@
 /* eslint-disable no-console,global-require,no-loop-func,func-names */
+const { Buffer } = require('buffer');
 const path = require('path');
 const fs = require('fs');
 const {
@@ -363,8 +364,8 @@ if (browser) {
               const done = rest[rest.length - 1];
               try {
                 const testArgs = rest.slice(0, rest.length - 1);
-                const test = window.getStep(scopedType, scopedName);
-                await test.apply(window.testWorld, testArgs);
+                const test = window.getStep(scopedType, scopedName); // eslint-disable-line no-undef
+                await test.apply(window.testWorld, testArgs); // eslint-disable-line no-undef
                 done({ error: null });
               } catch (err) {
                 console.error(err);
diff --git a/tests/cucumber/steps/steps.js b/tests/cucumber/steps/steps.js
index 56af3f7..c21b043 100644
--- a/tests/cucumber/steps/steps.js
+++ b/tests/cucumber/steps/steps.js
@@ -1,4 +1,5 @@
 /* eslint-disable func-names,radix */
+const { Buffer } = require('buffer');
 const assert = require('assert');
 const fs = require('fs');
 const path = require('path');
@@ -4549,6 +4550,20 @@ module.exports = function getSteps(options) {
     }
   );
 
+  Then(
+    'disassembly of {string} matches {string}',
+    async function (bytecodeFilename, sourceFilename) {
+      const bytecode = await loadResource(bytecodeFilename);
+      const resp = await this.v2Client.disassemble(bytecode).do();
+      const expectedSource = await loadResource(sourceFilename);
+
+      assert.deepStrictEqual(
+        resp.result.toString('UTF-8'),
+        expectedSource.toString('UTF-8')
+      );
+    }
+  );
+
   When(
     'we make a GetLightBlockHeaderProof call for round {int}',
     async function (int) {
diff --git a/tests/mocha.js b/tests/mocha.js
index 666ab50..d2202ea 100644
--- a/tests/mocha.js
+++ b/tests/mocha.js
@@ -1,3 +1,4 @@
+/* eslint-env node, mocha */
 /* eslint-disable no-console */
 const Mocha = require('mocha');
 const webpack = require('webpack');