Repository: algorand/js-algorand-sdk
Pull request #733 (2023-01-13 23:31:47)

Packaging: Don't use global `Buffer` object
## Summary

This PR makes the SDK no longer rely on a global `Buffer` object. See #708 for more context and for why this caused undesirable behavior.

I'm proud to report that this PR allows the SDK to be installed in a `create-react-app` project without ejecting or any additional configuration changes.*

## Specifics

My comment in https://github.com/algorand/js-algorand-sdk/pull/730#issuecomment-1379639585 expands on how our previous behavior causes problems for `create-react-app`.

Now, instead of using the global `Buffer` variable in our code and relying on a webpack plugin to set it up properly, we explicitly import `Buffer` from the `buffer` package. On node, this gives us the same thing as the global `Buffer` variable, and on the browser, this gives us our in-browser [buffer](https://www.npmjs.com/package/buffer) package.

> Note: the fact that the npm browser package is named `buffer` is convenient but not necessary. We could move to a differently named package if we wanted to (and we might, since [buffer](https://www.npmjs.com/package/buffer) has not been updated in some time). We'd just need to add the field `"buffer": "other_package_name"` to the `browser` object in our `package.json` and webpack will be able to substitute the new package when we import `buffer`.

In order to enforce this change in our repo, I made some changes to our eslint rules. The new rule `no-restricted-globals` causes eslint to fail if it detects any reference to the global `Buffer` variable, which should catch any problems going forward.

--- 

*There's one situation which unfortunately we can't solve in this SDK. Webpack v4 projects forcibly import an old version of the npm `buffer` package. Despite npm installing the higher version of `buffer` that we've specified in our package.json, webpack gives us the old one. I've given up trying to get around this for now, and I've changed the offending code causing #730 to use DataViews instead of Buffer. This solves the immediate problem, but it's possible other issues may arise from using an older version of `buffer` with our library.

I've opened #734 as a possible longer term solution to this problem.

barnjamin: FWIW I just tested against a fresh `create-react-app` and used `npm link` against this pr branch and needed no config changes to include and use the algosdk (yay!)

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/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..29fa26a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,7 @@
       "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..989b5d5 100644
--- a/package.json
+++ b/package.json
@@ -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/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/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 1376977..7fba1d3 100644
--- a/src/composer.ts
+++ b/src/composer.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'buffer';
 import {
   ABIType,
   ABITupleType,
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 2e778e2..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,
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/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..ec320ed 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');
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');