From 566285b30f661e1c7230f0502adfbc48fc96857a Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 29 Nov 2023 15:38:58 +0100 Subject: [PATCH 01/10] Migrate math tests to ethers.js v6 --- test/helpers/enums.js | 24 +- test/utils/math/Math.test.js | 494 ++++++++++++++--------------- test/utils/math/SafeCast.test.js | 155 +++++---- test/utils/math/SignedMath.test.js | 91 ++---- 4 files changed, 346 insertions(+), 418 deletions(-) diff --git a/test/helpers/enums.js b/test/helpers/enums.js index 6280e0f319b..b75e73ba8dc 100644 --- a/test/helpers/enums.js +++ b/test/helpers/enums.js @@ -2,10 +2,20 @@ function Enum(...options) { return Object.fromEntries(options.map((key, i) => [key, web3.utils.toBN(i)])); } -module.exports = { - Enum, - ProposalState: Enum('Pending', 'Active', 'Canceled', 'Defeated', 'Succeeded', 'Queued', 'Expired', 'Executed'), - VoteType: Enum('Against', 'For', 'Abstain'), - Rounding: Enum('Floor', 'Ceil', 'Trunc', 'Expand'), - OperationState: Enum('Unset', 'Waiting', 'Ready', 'Done'), -}; +function EnumBigInt(...options) { + return Object.fromEntries(options.map((key, i) => [key, BigInt(i)])); +} + +// TODO: remove web3, simplify code +function createExport(Enum) { + return { + Enum, + ProposalState: Enum('Pending', 'Active', 'Canceled', 'Defeated', 'Succeeded', 'Queued', 'Expired', 'Executed'), + VoteType: Enum('Against', 'For', 'Abstain'), + Rounding: Enum('Floor', 'Ceil', 'Trunc', 'Expand'), + OperationState: Enum('Unset', 'Waiting', 'Ready', 'Done'), + }; +} + +module.exports = createExport(Enum); +module.exports.bigint = createExport(EnumBigInt); diff --git a/test/utils/math/Math.test.js b/test/utils/math/Math.test.js index 7d4a58c81b1..1bbd1b94db7 100644 --- a/test/utils/math/Math.test.js +++ b/test/utils/math/Math.test.js @@ -1,287 +1,269 @@ -const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers'); +const { ethers } = require('hardhat'); const { expect } = require('chai'); -const { MAX_UINT256 } = constants; -const { Rounding } = require('../../helpers/enums.js'); -const { expectRevertCustomError } = require('../../helpers/customError.js'); - -const Math = artifacts.require('$Math'); +const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); +const { min, max } = require('../../helpers/math'); +const { + bigint: { Rounding }, +} = require('../../helpers/enums.js'); const RoundingDown = [Rounding.Floor, Rounding.Trunc]; const RoundingUp = [Rounding.Ceil, Rounding.Expand]; -function expectStruct(value, expected) { - for (const key in expected) { - if (BN.isBN(value[key])) { - expect(value[key]).to.be.bignumber.equal(expected[key]); - } else { - expect(value[key]).to.be.equal(expected[key]); - } - } +async function testCommutative(fn, lhs, rhs, expected, ...extra) { + expect(await fn(lhs, rhs, ...extra)).to.deep.equal(expected); + expect(await fn(rhs, lhs, ...extra)).to.deep.equal(expected); } -async function testCommutativeIterable(fn, lhs, rhs, expected, ...extra) { - expectStruct(await fn(lhs, rhs, ...extra), expected); - expectStruct(await fn(rhs, lhs, ...extra), expected); -} +async function fixture() { + const mock = await ethers.deployContract('$Math'); -contract('Math', function () { - const min = new BN('1234'); - const max = new BN('5678'); - const MAX_UINT256_SUB1 = MAX_UINT256.sub(new BN('1')); - const MAX_UINT256_SUB2 = MAX_UINT256.sub(new BN('2')); + // disambiguation, we use the version with explicit rounding + mock.$mulDiv = mock['$mulDiv(uint256,uint256,uint256,uint8)']; + mock.$sqrt = mock['$sqrt(uint256,uint8)']; + mock.$log2 = mock['$log2(uint256,uint8)']; + mock.$log10 = mock['$log10(uint256,uint8)']; + mock.$log256 = mock['$log256(uint256,uint8)']; + return { mock }; +} + +describe('Math', function () { beforeEach(async function () { - this.math = await Math.new(); + Object.assign(this, await loadFixture(fixture)); }); describe('tryAdd', function () { it('adds correctly', async function () { - const a = new BN('5678'); - const b = new BN('1234'); - - await testCommutativeIterable(this.math.$tryAdd, a, b, [true, a.add(b)]); + const a = 5678n; + const b = 1234n; + await testCommutative(this.mock.$tryAdd, a, b, [true, a + b]); }); it('reverts on addition overflow', async function () { - const a = MAX_UINT256; - const b = new BN('1'); - - await testCommutativeIterable(this.math.$tryAdd, a, b, [false, '0']); + const a = ethers.MaxUint256; + const b = 1n; + await testCommutative(this.mock.$tryAdd, a, b, [false, 0n]); }); }); describe('trySub', function () { it('subtracts correctly', async function () { - const a = new BN('5678'); - const b = new BN('1234'); - - expectStruct(await this.math.$trySub(a, b), [true, a.sub(b)]); + const a = 5678n; + const b = 1234n; + expect(await this.mock.$trySub(a, b)).to.deep.equal([true, a - b]); }); it('reverts if subtraction result would be negative', async function () { - const a = new BN('1234'); - const b = new BN('5678'); - - expectStruct(await this.math.$trySub(a, b), [false, '0']); + const a = 1234n; + const b = 5678n; + expect(await this.mock.$trySub(a, b)).to.deep.equal([false, 0n]); }); }); describe('tryMul', function () { it('multiplies correctly', async function () { - const a = new BN('1234'); - const b = new BN('5678'); - - await testCommutativeIterable(this.math.$tryMul, a, b, [true, a.mul(b)]); + const a = 1234n; + const b = 5678n; + await testCommutative(this.mock.$tryMul, a, b, [true, a * b]); }); it('multiplies by zero correctly', async function () { - const a = new BN('0'); - const b = new BN('5678'); - - await testCommutativeIterable(this.math.$tryMul, a, b, [true, a.mul(b)]); + const a = 0n; + const b = 5678n; + await testCommutative(this.mock.$tryMul, a, b, [true, a * b]); }); it('reverts on multiplication overflow', async function () { - const a = MAX_UINT256; - const b = new BN('2'); - - await testCommutativeIterable(this.math.$tryMul, a, b, [false, '0']); + const a = ethers.MaxUint256; + const b = 2n; + await testCommutative(this.mock.$tryMul, a, b, [false, 0n]); }); }); describe('tryDiv', function () { it('divides correctly', async function () { - const a = new BN('5678'); - const b = new BN('5678'); - - expectStruct(await this.math.$tryDiv(a, b), [true, a.div(b)]); + const a = 5678n; + const b = 5678n; + expect(await this.mock.$tryDiv(a, b)).to.deep.equal([true, a / b]); }); it('divides zero correctly', async function () { - const a = new BN('0'); - const b = new BN('5678'); - - expectStruct(await this.math.$tryDiv(a, b), [true, a.div(b)]); + const a = 0n; + const b = 5678n; + expect(await this.mock.$tryDiv(a, b)).to.deep.equal([true, a / b]); }); it('returns complete number result on non-even division', async function () { - const a = new BN('7000'); - const b = new BN('5678'); - - expectStruct(await this.math.$tryDiv(a, b), [true, a.div(b)]); + const a = 7000n; + const b = 5678n; + expect(await this.mock.$tryDiv(a, b)).to.deep.equal([true, a / b]); }); it('reverts on division by zero', async function () { - const a = new BN('5678'); - const b = new BN('0'); - - expectStruct(await this.math.$tryDiv(a, b), [false, '0']); + const a = 5678n; + const b = 0n; + expect(await this.mock.$tryDiv(a, b)).to.deep.equal([false, 0n]); }); }); describe('tryMod', function () { describe('modulos correctly', async function () { it('when the dividend is smaller than the divisor', async function () { - const a = new BN('284'); - const b = new BN('5678'); - - expectStruct(await this.math.$tryMod(a, b), [true, a.mod(b)]); + const a = 284n; + const b = 5678n; + expect(await this.mock.$tryMod(a, b)).to.deep.equal([true, a % b]); }); it('when the dividend is equal to the divisor', async function () { - const a = new BN('5678'); - const b = new BN('5678'); - - expectStruct(await this.math.$tryMod(a, b), [true, a.mod(b)]); + const a = 5678n; + const b = 5678n; + expect(await this.mock.$tryMod(a, b)).to.deep.equal([true, a % b]); }); it('when the dividend is larger than the divisor', async function () { - const a = new BN('7000'); - const b = new BN('5678'); - - expectStruct(await this.math.$tryMod(a, b), [true, a.mod(b)]); + const a = 7000n; + const b = 5678n; + expect(await this.mock.$tryMod(a, b)).to.deep.equal([true, a % b]); }); it('when the dividend is a multiple of the divisor', async function () { - const a = new BN('17034'); // 17034 == 5678 * 3 - const b = new BN('5678'); - - expectStruct(await this.math.$tryMod(a, b), [true, a.mod(b)]); + const a = 17034n; // 17034 == 5678 * 3 + const b = 5678n; + expect(await this.mock.$tryMod(a, b)).to.deep.equal([true, a % b]); }); }); it('reverts with a 0 divisor', async function () { - const a = new BN('5678'); - const b = new BN('0'); - - expectStruct(await this.math.$tryMod(a, b), [false, '0']); + const a = 5678n; + const b = 0n; + expect(await this.mock.$tryMod(a, b)).to.deep.equal([false, 0n]); }); }); describe('max', function () { - it('is correctly detected in first argument position', async function () { - expect(await this.math.$max(max, min)).to.be.bignumber.equal(max); - }); - - it('is correctly detected in second argument position', async function () { - expect(await this.math.$max(min, max)).to.be.bignumber.equal(max); + it('is correctly detected in both position', async function () { + testCommutative(this.mock.$max, 1234n, 5678n, [max(1234n, 5678n)]); }); }); describe('min', function () { - it('is correctly detected in first argument position', async function () { - expect(await this.math.$min(min, max)).to.be.bignumber.equal(min); - }); - - it('is correctly detected in second argument position', async function () { - expect(await this.math.$min(max, min)).to.be.bignumber.equal(min); + it('is correctly detected in both position', async function () { + testCommutative(this.mock.$min, 1234n, 5678n, [min(1234n, 5678n)]); }); }); describe('average', function () { - function bnAverage(a, b) { - return a.add(b).divn(2); - } - it('is correctly calculated with two odd numbers', async function () { - const a = new BN('57417'); - const b = new BN('95431'); - expect(await this.math.$average(a, b)).to.be.bignumber.equal(bnAverage(a, b)); + const a = 57417n; + const b = 95431n; + expect(await this.mock.$average(a, b)).to.equal((a + b) / 2n); }); it('is correctly calculated with two even numbers', async function () { - const a = new BN('42304'); - const b = new BN('84346'); - expect(await this.math.$average(a, b)).to.be.bignumber.equal(bnAverage(a, b)); + const a = 42304n; + const b = 84346n; + expect(await this.mock.$average(a, b)).to.equal((a + b) / 2n); }); it('is correctly calculated with one even and one odd number', async function () { - const a = new BN('57417'); - const b = new BN('84346'); - expect(await this.math.$average(a, b)).to.be.bignumber.equal(bnAverage(a, b)); + const a = 57417n; + const b = 84346n; + expect(await this.mock.$average(a, b)).to.equal((a + b) / 2n); }); it('is correctly calculated with two max uint256 numbers', async function () { - const a = MAX_UINT256; - expect(await this.math.$average(a, a)).to.be.bignumber.equal(bnAverage(a, a)); + const a = ethers.MaxUint256; + expect(await this.mock.$average(a, a)).to.equal(a); }); }); describe('ceilDiv', function () { it('reverts on zero division', async function () { - const a = new BN('2'); - const b = new BN('0'); + const a = 2n; + const b = 0n; // It's unspecified because it's a low level 0 division error - await expectRevert.unspecified(this.math.$ceilDiv(a, b)); + await expect(this.mock.$ceilDiv(a, b)).to.be.revertedWithPanic(PANIC_CODES.DIVISION_BY_ZERO); }); it('does not round up a zero result', async function () { - const a = new BN('0'); - const b = new BN('2'); - expect(await this.math.$ceilDiv(a, b)).to.be.bignumber.equal('0'); + const a = 0n; + const b = 2n; + const r = 0n; + expect(await this.mock.$ceilDiv(a, b)).to.equal(r); }); it('does not round up on exact division', async function () { - const a = new BN('10'); - const b = new BN('5'); - expect(await this.math.$ceilDiv(a, b)).to.be.bignumber.equal('2'); + const a = 10n; + const b = 5n; + const r = 2n; + expect(await this.mock.$ceilDiv(a, b)).to.equal(r); }); it('rounds up on division with remainders', async function () { - const a = new BN('42'); - const b = new BN('13'); - expect(await this.math.$ceilDiv(a, b)).to.be.bignumber.equal('4'); + const a = 42n; + const b = 13n; + const r = 4n; + expect(await this.mock.$ceilDiv(a, b)).to.equal(r); }); it('does not overflow', async function () { - const b = new BN('2'); - const result = new BN('1').shln(255); - expect(await this.math.$ceilDiv(MAX_UINT256, b)).to.be.bignumber.equal(result); + const a = ethers.MaxUint256; + const b = 2n; + const r = 1n << 255n; + expect(await this.mock.$ceilDiv(a, b)).to.equal(r); }); it('correctly computes max uint256 divided by 1', async function () { - const b = new BN('1'); - expect(await this.math.$ceilDiv(MAX_UINT256, b)).to.be.bignumber.equal(MAX_UINT256); + const a = ethers.MaxUint256; + const b = 1n; + const r = ethers.MaxUint256; + expect(await this.mock.$ceilDiv(a, b)).to.equal(r); }); }); describe('muldiv', function () { it('divide by 0', async function () { - await expectRevert.unspecified(this.math.$mulDiv(1, 1, 0, Rounding.Floor)); + const a = 1n; + const b = 1n; + const c = 0n; + await expect(this.mock.$mulDiv(a, b, c, Rounding.Floor)).to.be.revertedWithPanic(PANIC_CODES.DIVISION_BY_ZERO); }); it('reverts with result higher than 2 ^ 256', async function () { - await expectRevertCustomError(this.math.$mulDiv(5, MAX_UINT256, 2, Rounding.Floor), 'MathOverflowedMulDiv', []); + const a = 5n; + const b = ethers.MaxUint256; + const c = 2n; + await expect(this.mock.$mulDiv(a, b, c, Rounding.Floor)).to.be.revertedWithCustomError( + this.mock, + 'MathOverflowedMulDiv', + ); }); describe('does round down', async function () { it('small values', async function () { for (const rounding of RoundingDown) { - expect(await this.math.$mulDiv('3', '4', '5', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.$mulDiv('3', '5', '5', rounding)).to.be.bignumber.equal('3'); + expect(await this.mock.$mulDiv(3n, 4n, 5n, rounding)).to.equal(2n); + expect(await this.mock.$mulDiv(3n, 5n, 5n, rounding)).to.equal(3n); } }); it('large values', async function () { for (const rounding of RoundingDown) { - expect(await this.math.$mulDiv(new BN('42'), MAX_UINT256_SUB1, MAX_UINT256, rounding)).to.be.bignumber.equal( - new BN('41'), - ); + expect(await this.mock.$mulDiv(42n, ethers.MaxUint256 - 1n, ethers.MaxUint256, rounding)).to.equal(41n); - expect(await this.math.$mulDiv(new BN('17'), MAX_UINT256, MAX_UINT256, rounding)).to.be.bignumber.equal( - new BN('17'), - ); + expect(await this.mock.$mulDiv(17n, ethers.MaxUint256, ethers.MaxUint256, rounding)).to.equal(17n); expect( - await this.math.$mulDiv(MAX_UINT256_SUB1, MAX_UINT256_SUB1, MAX_UINT256, rounding), - ).to.be.bignumber.equal(MAX_UINT256_SUB2); + await this.mock.$mulDiv(ethers.MaxUint256 - 1n, ethers.MaxUint256 - 1n, ethers.MaxUint256, rounding), + ).to.equal(ethers.MaxUint256 - 2n); - expect(await this.math.$mulDiv(MAX_UINT256, MAX_UINT256_SUB1, MAX_UINT256, rounding)).to.be.bignumber.equal( - MAX_UINT256_SUB1, - ); + expect( + await this.mock.$mulDiv(ethers.MaxUint256, ethers.MaxUint256 - 1n, ethers.MaxUint256, rounding), + ).to.equal(ethers.MaxUint256 - 1n); - expect(await this.math.$mulDiv(MAX_UINT256, MAX_UINT256, MAX_UINT256, rounding)).to.be.bignumber.equal( - MAX_UINT256, + expect(await this.mock.$mulDiv(ethers.MaxUint256, ethers.MaxUint256, ethers.MaxUint256, rounding)).to.equal( + ethers.MaxUint256, ); } }); @@ -290,31 +272,27 @@ contract('Math', function () { describe('does round up', async function () { it('small values', async function () { for (const rounding of RoundingUp) { - expect(await this.math.$mulDiv('3', '4', '5', rounding)).to.be.bignumber.equal('3'); - expect(await this.math.$mulDiv('3', '5', '5', rounding)).to.be.bignumber.equal('3'); + expect(await this.mock.$mulDiv(3n, 4n, 5n, rounding)).to.equal(3n); + expect(await this.mock.$mulDiv(3n, 5n, 5n, rounding)).to.equal(3n); } }); it('large values', async function () { for (const rounding of RoundingUp) { - expect(await this.math.$mulDiv(new BN('42'), MAX_UINT256_SUB1, MAX_UINT256, rounding)).to.be.bignumber.equal( - new BN('42'), - ); + expect(await this.mock.$mulDiv(42n, ethers.MaxUint256 - 1n, ethers.MaxUint256, rounding)).to.equal(42n); - expect(await this.math.$mulDiv(new BN('17'), MAX_UINT256, MAX_UINT256, rounding)).to.be.bignumber.equal( - new BN('17'), - ); + expect(await this.mock.$mulDiv(17n, ethers.MaxUint256, ethers.MaxUint256, rounding)).to.equal(17n); expect( - await this.math.$mulDiv(MAX_UINT256_SUB1, MAX_UINT256_SUB1, MAX_UINT256, rounding), - ).to.be.bignumber.equal(MAX_UINT256_SUB1); + await this.mock.$mulDiv(ethers.MaxUint256 - 1n, ethers.MaxUint256 - 1n, ethers.MaxUint256, rounding), + ).to.equal(ethers.MaxUint256 - 1n); - expect(await this.math.$mulDiv(MAX_UINT256, MAX_UINT256_SUB1, MAX_UINT256, rounding)).to.be.bignumber.equal( - MAX_UINT256_SUB1, - ); + expect( + await this.mock.$mulDiv(ethers.MaxUint256, ethers.MaxUint256 - 1n, ethers.MaxUint256, rounding), + ).to.equal(ethers.MaxUint256 - 1n); - expect(await this.math.$mulDiv(MAX_UINT256, MAX_UINT256, MAX_UINT256, rounding)).to.be.bignumber.equal( - MAX_UINT256, + expect(await this.mock.$mulDiv(ethers.MaxUint256, ethers.MaxUint256, ethers.MaxUint256, rounding)).to.equal( + ethers.MaxUint256, ); } }); @@ -324,39 +302,35 @@ contract('Math', function () { describe('sqrt', function () { it('rounds down', async function () { for (const rounding of RoundingDown) { - expect(await this.math.$sqrt('0', rounding)).to.be.bignumber.equal('0'); - expect(await this.math.$sqrt('1', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.$sqrt('2', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.$sqrt('3', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.$sqrt('4', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.$sqrt('144', rounding)).to.be.bignumber.equal('12'); - expect(await this.math.$sqrt('999999', rounding)).to.be.bignumber.equal('999'); - expect(await this.math.$sqrt('1000000', rounding)).to.be.bignumber.equal('1000'); - expect(await this.math.$sqrt('1000001', rounding)).to.be.bignumber.equal('1000'); - expect(await this.math.$sqrt('1002000', rounding)).to.be.bignumber.equal('1000'); - expect(await this.math.$sqrt('1002001', rounding)).to.be.bignumber.equal('1001'); - expect(await this.math.$sqrt(MAX_UINT256, rounding)).to.be.bignumber.equal( - '340282366920938463463374607431768211455', - ); + expect(await this.mock.$sqrt(0n, rounding)).to.equal(0n); + expect(await this.mock.$sqrt(1n, rounding)).to.equal(1n); + expect(await this.mock.$sqrt(2n, rounding)).to.equal(1n); + expect(await this.mock.$sqrt(3n, rounding)).to.equal(1n); + expect(await this.mock.$sqrt(4n, rounding)).to.equal(2n); + expect(await this.mock.$sqrt(144n, rounding)).to.equal(12n); + expect(await this.mock.$sqrt(999999n, rounding)).to.equal(999n); + expect(await this.mock.$sqrt(1000000n, rounding)).to.equal(1000n); + expect(await this.mock.$sqrt(1000001n, rounding)).to.equal(1000n); + expect(await this.mock.$sqrt(1002000n, rounding)).to.equal(1000n); + expect(await this.mock.$sqrt(1002001n, rounding)).to.equal(1001n); + expect(await this.mock.$sqrt(ethers.MaxUint256, rounding)).to.equal(340282366920938463463374607431768211455n); } }); it('rounds up', async function () { for (const rounding of RoundingUp) { - expect(await this.math.$sqrt('0', rounding)).to.be.bignumber.equal('0'); - expect(await this.math.$sqrt('1', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.$sqrt('2', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.$sqrt('3', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.$sqrt('4', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.$sqrt('144', rounding)).to.be.bignumber.equal('12'); - expect(await this.math.$sqrt('999999', rounding)).to.be.bignumber.equal('1000'); - expect(await this.math.$sqrt('1000000', rounding)).to.be.bignumber.equal('1000'); - expect(await this.math.$sqrt('1000001', rounding)).to.be.bignumber.equal('1001'); - expect(await this.math.$sqrt('1002000', rounding)).to.be.bignumber.equal('1001'); - expect(await this.math.$sqrt('1002001', rounding)).to.be.bignumber.equal('1001'); - expect(await this.math.$sqrt(MAX_UINT256, rounding)).to.be.bignumber.equal( - '340282366920938463463374607431768211456', - ); + expect(await this.mock.$sqrt(0n, rounding)).to.equal(0n); + expect(await this.mock.$sqrt(1n, rounding)).to.equal(1n); + expect(await this.mock.$sqrt(2n, rounding)).to.equal(2n); + expect(await this.mock.$sqrt(3n, rounding)).to.equal(2n); + expect(await this.mock.$sqrt(4n, rounding)).to.equal(2n); + expect(await this.mock.$sqrt(144n, rounding)).to.equal(12n); + expect(await this.mock.$sqrt(999999n, rounding)).to.equal(1000n); + expect(await this.mock.$sqrt(1000000n, rounding)).to.equal(1000n); + expect(await this.mock.$sqrt(1000001n, rounding)).to.equal(1001n); + expect(await this.mock.$sqrt(1002000n, rounding)).to.equal(1001n); + expect(await this.mock.$sqrt(1002001n, rounding)).to.equal(1001n); + expect(await this.mock.$sqrt(ethers.MaxUint256, rounding)).to.equal(340282366920938463463374607431768211456n); } }); }); @@ -365,33 +339,33 @@ contract('Math', function () { describe('log2', function () { it('rounds down', async function () { for (const rounding of RoundingDown) { - expect(await this.math.methods['$log2(uint256,uint8)']('0', rounding)).to.be.bignumber.equal('0'); - expect(await this.math.methods['$log2(uint256,uint8)']('1', rounding)).to.be.bignumber.equal('0'); - expect(await this.math.methods['$log2(uint256,uint8)']('2', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.methods['$log2(uint256,uint8)']('3', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.methods['$log2(uint256,uint8)']('4', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.methods['$log2(uint256,uint8)']('5', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.methods['$log2(uint256,uint8)']('6', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.methods['$log2(uint256,uint8)']('7', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.methods['$log2(uint256,uint8)']('8', rounding)).to.be.bignumber.equal('3'); - expect(await this.math.methods['$log2(uint256,uint8)']('9', rounding)).to.be.bignumber.equal('3'); - expect(await this.math.methods['$log2(uint256,uint8)'](MAX_UINT256, rounding)).to.be.bignumber.equal('255'); + expect(await this.mock.$log2(0n, rounding)).to.equal(0n); + expect(await this.mock.$log2(1n, rounding)).to.equal(0n); + expect(await this.mock.$log2(2n, rounding)).to.equal(1n); + expect(await this.mock.$log2(3n, rounding)).to.equal(1n); + expect(await this.mock.$log2(4n, rounding)).to.equal(2n); + expect(await this.mock.$log2(5n, rounding)).to.equal(2n); + expect(await this.mock.$log2(6n, rounding)).to.equal(2n); + expect(await this.mock.$log2(7n, rounding)).to.equal(2n); + expect(await this.mock.$log2(8n, rounding)).to.equal(3n); + expect(await this.mock.$log2(9n, rounding)).to.equal(3n); + expect(await this.mock.$log2(ethers.MaxUint256, rounding)).to.equal(255n); } }); it('rounds up', async function () { for (const rounding of RoundingUp) { - expect(await this.math.methods['$log2(uint256,uint8)']('0', rounding)).to.be.bignumber.equal('0'); - expect(await this.math.methods['$log2(uint256,uint8)']('1', rounding)).to.be.bignumber.equal('0'); - expect(await this.math.methods['$log2(uint256,uint8)']('2', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.methods['$log2(uint256,uint8)']('3', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.methods['$log2(uint256,uint8)']('4', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.methods['$log2(uint256,uint8)']('5', rounding)).to.be.bignumber.equal('3'); - expect(await this.math.methods['$log2(uint256,uint8)']('6', rounding)).to.be.bignumber.equal('3'); - expect(await this.math.methods['$log2(uint256,uint8)']('7', rounding)).to.be.bignumber.equal('3'); - expect(await this.math.methods['$log2(uint256,uint8)']('8', rounding)).to.be.bignumber.equal('3'); - expect(await this.math.methods['$log2(uint256,uint8)']('9', rounding)).to.be.bignumber.equal('4'); - expect(await this.math.methods['$log2(uint256,uint8)'](MAX_UINT256, rounding)).to.be.bignumber.equal('256'); + expect(await this.mock.$log2(0n, rounding)).to.equal(0n); + expect(await this.mock.$log2(1n, rounding)).to.equal(0n); + expect(await this.mock.$log2(2n, rounding)).to.equal(1n); + expect(await this.mock.$log2(3n, rounding)).to.equal(2n); + expect(await this.mock.$log2(4n, rounding)).to.equal(2n); + expect(await this.mock.$log2(5n, rounding)).to.equal(3n); + expect(await this.mock.$log2(6n, rounding)).to.equal(3n); + expect(await this.mock.$log2(7n, rounding)).to.equal(3n); + expect(await this.mock.$log2(8n, rounding)).to.equal(3n); + expect(await this.mock.$log2(9n, rounding)).to.equal(4n); + expect(await this.mock.$log2(ethers.MaxUint256, rounding)).to.equal(256n); } }); }); @@ -399,37 +373,37 @@ contract('Math', function () { describe('log10', function () { it('rounds down', async function () { for (const rounding of RoundingDown) { - expect(await this.math.$log10('0', rounding)).to.be.bignumber.equal('0'); - expect(await this.math.$log10('1', rounding)).to.be.bignumber.equal('0'); - expect(await this.math.$log10('2', rounding)).to.be.bignumber.equal('0'); - expect(await this.math.$log10('9', rounding)).to.be.bignumber.equal('0'); - expect(await this.math.$log10('10', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.$log10('11', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.$log10('99', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.$log10('100', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.$log10('101', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.$log10('999', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.$log10('1000', rounding)).to.be.bignumber.equal('3'); - expect(await this.math.$log10('1001', rounding)).to.be.bignumber.equal('3'); - expect(await this.math.$log10(MAX_UINT256, rounding)).to.be.bignumber.equal('77'); + expect(await this.mock.$log10(0n, rounding)).to.equal(0n); + expect(await this.mock.$log10(1n, rounding)).to.equal(0n); + expect(await this.mock.$log10(2n, rounding)).to.equal(0n); + expect(await this.mock.$log10(9n, rounding)).to.equal(0n); + expect(await this.mock.$log10(10n, rounding)).to.equal(1n); + expect(await this.mock.$log10(11n, rounding)).to.equal(1n); + expect(await this.mock.$log10(99n, rounding)).to.equal(1n); + expect(await this.mock.$log10(100n, rounding)).to.equal(2n); + expect(await this.mock.$log10(101n, rounding)).to.equal(2n); + expect(await this.mock.$log10(999n, rounding)).to.equal(2n); + expect(await this.mock.$log10(1000n, rounding)).to.equal(3n); + expect(await this.mock.$log10(1001n, rounding)).to.equal(3n); + expect(await this.mock.$log10(ethers.MaxUint256, rounding)).to.equal(77n); } }); it('rounds up', async function () { for (const rounding of RoundingUp) { - expect(await this.math.$log10('0', rounding)).to.be.bignumber.equal('0'); - expect(await this.math.$log10('1', rounding)).to.be.bignumber.equal('0'); - expect(await this.math.$log10('2', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.$log10('9', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.$log10('10', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.$log10('11', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.$log10('99', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.$log10('100', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.$log10('101', rounding)).to.be.bignumber.equal('3'); - expect(await this.math.$log10('999', rounding)).to.be.bignumber.equal('3'); - expect(await this.math.$log10('1000', rounding)).to.be.bignumber.equal('3'); - expect(await this.math.$log10('1001', rounding)).to.be.bignumber.equal('4'); - expect(await this.math.$log10(MAX_UINT256, rounding)).to.be.bignumber.equal('78'); + expect(await this.mock.$log10(0n, rounding)).to.equal(0n); + expect(await this.mock.$log10(1n, rounding)).to.equal(0n); + expect(await this.mock.$log10(2n, rounding)).to.equal(1n); + expect(await this.mock.$log10(9n, rounding)).to.equal(1n); + expect(await this.mock.$log10(10n, rounding)).to.equal(1n); + expect(await this.mock.$log10(11n, rounding)).to.equal(2n); + expect(await this.mock.$log10(99n, rounding)).to.equal(2n); + expect(await this.mock.$log10(100n, rounding)).to.equal(2n); + expect(await this.mock.$log10(101n, rounding)).to.equal(3n); + expect(await this.mock.$log10(999n, rounding)).to.equal(3n); + expect(await this.mock.$log10(1000n, rounding)).to.equal(3n); + expect(await this.mock.$log10(1001n, rounding)).to.equal(4n); + expect(await this.mock.$log10(ethers.MaxUint256, rounding)).to.equal(78n); } }); }); @@ -437,31 +411,31 @@ contract('Math', function () { describe('log256', function () { it('rounds down', async function () { for (const rounding of RoundingDown) { - expect(await this.math.$log256('0', rounding)).to.be.bignumber.equal('0'); - expect(await this.math.$log256('1', rounding)).to.be.bignumber.equal('0'); - expect(await this.math.$log256('2', rounding)).to.be.bignumber.equal('0'); - expect(await this.math.$log256('255', rounding)).to.be.bignumber.equal('0'); - expect(await this.math.$log256('256', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.$log256('257', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.$log256('65535', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.$log256('65536', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.$log256('65537', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.$log256(MAX_UINT256, rounding)).to.be.bignumber.equal('31'); + expect(await this.mock.$log256(0n, rounding)).to.equal(0n); + expect(await this.mock.$log256(1n, rounding)).to.equal(0n); + expect(await this.mock.$log256(2n, rounding)).to.equal(0n); + expect(await this.mock.$log256(255n, rounding)).to.equal(0n); + expect(await this.mock.$log256(256n, rounding)).to.equal(1n); + expect(await this.mock.$log256(257n, rounding)).to.equal(1n); + expect(await this.mock.$log256(65535n, rounding)).to.equal(1n); + expect(await this.mock.$log256(65536n, rounding)).to.equal(2n); + expect(await this.mock.$log256(65537n, rounding)).to.equal(2n); + expect(await this.mock.$log256(ethers.MaxUint256, rounding)).to.equal(31n); } }); it('rounds up', async function () { for (const rounding of RoundingUp) { - expect(await this.math.$log256('0', rounding)).to.be.bignumber.equal('0'); - expect(await this.math.$log256('1', rounding)).to.be.bignumber.equal('0'); - expect(await this.math.$log256('2', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.$log256('255', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.$log256('256', rounding)).to.be.bignumber.equal('1'); - expect(await this.math.$log256('257', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.$log256('65535', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.$log256('65536', rounding)).to.be.bignumber.equal('2'); - expect(await this.math.$log256('65537', rounding)).to.be.bignumber.equal('3'); - expect(await this.math.$log256(MAX_UINT256, rounding)).to.be.bignumber.equal('32'); + expect(await this.mock.$log256(0n, rounding)).to.equal(0n); + expect(await this.mock.$log256(1n, rounding)).to.equal(0n); + expect(await this.mock.$log256(2n, rounding)).to.equal(1n); + expect(await this.mock.$log256(255n, rounding)).to.equal(1n); + expect(await this.mock.$log256(256n, rounding)).to.equal(1n); + expect(await this.mock.$log256(257n, rounding)).to.equal(2n); + expect(await this.mock.$log256(65535n, rounding)).to.equal(2n); + expect(await this.mock.$log256(65536n, rounding)).to.equal(2n); + expect(await this.mock.$log256(65537n, rounding)).to.equal(3n); + expect(await this.mock.$log256(ethers.MaxUint256, rounding)).to.equal(32n); } }); }); diff --git a/test/utils/math/SafeCast.test.js b/test/utils/math/SafeCast.test.js index 4b8ec5a7203..c081524c137 100644 --- a/test/utils/math/SafeCast.test.js +++ b/test/utils/math/SafeCast.test.js @@ -1,161 +1,148 @@ -const { BN } = require('@openzeppelin/test-helpers'); +const { ethers } = require('hardhat'); const { expect } = require('chai'); +const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { range } = require('../../../scripts/helpers'); -const { expectRevertCustomError } = require('../../helpers/customError'); -const SafeCast = artifacts.require('$SafeCast'); +async function fixture() { + const mock = await ethers.deployContract('$SafeCast'); + return { mock }; +} contract('SafeCast', async function () { beforeEach(async function () { - this.safeCast = await SafeCast.new(); + Object.assign(this, await loadFixture(fixture)); }); - function testToUint(bits) { - describe(`toUint${bits}`, () => { - const maxValue = new BN('2').pow(new BN(bits)).subn(1); + for (const bits of range(8, 256, 8).map(ethers.toBigInt)) { + const maxValue = 2n ** bits - 1n; + describe(`toUint${bits}`, () => { it('downcasts 0', async function () { - expect(await this.safeCast[`$toUint${bits}`](0)).to.be.bignumber.equal('0'); + expect(await this.mock[`$toUint${bits}`](0n)).is.equal(0n); }); it('downcasts 1', async function () { - expect(await this.safeCast[`$toUint${bits}`](1)).to.be.bignumber.equal('1'); + expect(await this.mock[`$toUint${bits}`](1n)).is.equal(1n); }); it(`downcasts 2^${bits} - 1 (${maxValue})`, async function () { - expect(await this.safeCast[`$toUint${bits}`](maxValue)).to.be.bignumber.equal(maxValue); + expect(await this.mock[`$toUint${bits}`](maxValue)).is.equal(maxValue); }); - it(`reverts when downcasting 2^${bits} (${maxValue.addn(1)})`, async function () { - await expectRevertCustomError( - this.safeCast[`$toUint${bits}`](maxValue.addn(1)), - `SafeCastOverflowedUintDowncast`, - [bits, maxValue.addn(1)], - ); + it(`reverts when downcasting 2^${bits} (${maxValue + 1n})`, async function () { + await expect(this.mock[`$toUint${bits}`](maxValue + 1n)) + .to.be.revertedWithCustomError(this.mock, 'SafeCastOverflowedUintDowncast') + .withArgs(bits, maxValue + 1n); }); - it(`reverts when downcasting 2^${bits} + 1 (${maxValue.addn(2)})`, async function () { - await expectRevertCustomError( - this.safeCast[`$toUint${bits}`](maxValue.addn(2)), - `SafeCastOverflowedUintDowncast`, - [bits, maxValue.addn(2)], - ); + it(`reverts when downcasting 2^${bits} + 1 (${maxValue + 2n})`, async function () { + await expect(this.mock[`$toUint${bits}`](maxValue + 2n)) + .to.be.revertedWithCustomError(this.mock, 'SafeCastOverflowedUintDowncast') + .withArgs(bits, maxValue + 2n); }); }); } - range(8, 256, 8).forEach(bits => testToUint(bits)); - describe('toUint256', () => { - const maxInt256 = new BN('2').pow(new BN(255)).subn(1); - const minInt256 = new BN('2').pow(new BN(255)).neg(); - it('casts 0', async function () { - expect(await this.safeCast.$toUint256(0)).to.be.bignumber.equal('0'); + expect(await this.mock.$toUint256(0n)).is.equal(0n); }); it('casts 1', async function () { - expect(await this.safeCast.$toUint256(1)).to.be.bignumber.equal('1'); + expect(await this.mock.$toUint256(1n)).is.equal(1n); }); - it(`casts INT256_MAX (${maxInt256})`, async function () { - expect(await this.safeCast.$toUint256(maxInt256)).to.be.bignumber.equal(maxInt256); + it(`casts INT256_MAX (${ethers.MaxInt256})`, async function () { + expect(await this.mock.$toUint256(ethers.MaxInt256)).is.equal(ethers.MaxInt256); }); it('reverts when casting -1', async function () { - await expectRevertCustomError(this.safeCast.$toUint256(-1), `SafeCastOverflowedIntToUint`, [-1]); + await expect(this.mock.$toUint256(-1n)) + .to.be.revertedWithCustomError(this.mock, `SafeCastOverflowedIntToUint`) + .withArgs(-1n); }); - it(`reverts when casting INT256_MIN (${minInt256})`, async function () { - await expectRevertCustomError(this.safeCast.$toUint256(minInt256), `SafeCastOverflowedIntToUint`, [minInt256]); + it(`reverts when casting INT256_MIN (${ethers.MinInt256})`, async function () { + await expect(this.mock.$toUint256(ethers.MinInt256)) + .to.be.revertedWithCustomError(this.mock, `SafeCastOverflowedIntToUint`) + .withArgs(ethers.MinInt256); }); }); - function testToInt(bits) { - describe(`toInt${bits}`, () => { - const minValue = new BN('-2').pow(new BN(bits - 1)); - const maxValue = new BN('2').pow(new BN(bits - 1)).subn(1); + for (const bits of range(8, 256, 8).map(ethers.toBigInt)) { + const minValue = -(2n ** (bits - 1n)); + const maxValue = 2n ** (bits - 1n) - 1n; + describe(`toInt${bits}`, () => { it('downcasts 0', async function () { - expect(await this.safeCast[`$toInt${bits}`](0)).to.be.bignumber.equal('0'); + expect(await this.mock[`$toInt${bits}`](0n)).is.equal(0n); }); it('downcasts 1', async function () { - expect(await this.safeCast[`$toInt${bits}`](1)).to.be.bignumber.equal('1'); + expect(await this.mock[`$toInt${bits}`](1n)).is.equal(1n); }); it('downcasts -1', async function () { - expect(await this.safeCast[`$toInt${bits}`](-1)).to.be.bignumber.equal('-1'); + expect(await this.mock[`$toInt${bits}`](-1n)).is.equal(-1n); }); - it(`downcasts -2^${bits - 1} (${minValue})`, async function () { - expect(await this.safeCast[`$toInt${bits}`](minValue)).to.be.bignumber.equal(minValue); + it(`downcasts -2^${bits - 1n} (${minValue})`, async function () { + expect(await this.mock[`$toInt${bits}`](minValue)).is.equal(minValue); }); - it(`downcasts 2^${bits - 1} - 1 (${maxValue})`, async function () { - expect(await this.safeCast[`$toInt${bits}`](maxValue)).to.be.bignumber.equal(maxValue); + it(`downcasts 2^${bits - 1n} - 1 (${maxValue})`, async function () { + expect(await this.mock[`$toInt${bits}`](maxValue)).is.equal(maxValue); }); - it(`reverts when downcasting -2^${bits - 1} - 1 (${minValue.subn(1)})`, async function () { - await expectRevertCustomError( - this.safeCast[`$toInt${bits}`](minValue.subn(1)), - `SafeCastOverflowedIntDowncast`, - [bits, minValue.subn(1)], - ); + it(`reverts when downcasting -2^${bits - 1n} - 1 (${minValue - 1n})`, async function () { + await expect(this.mock[`$toInt${bits}`](minValue - 1n)) + .to.be.revertedWithCustomError(this.mock, 'SafeCastOverflowedIntDowncast') + .withArgs(bits, minValue - 1n); }); - it(`reverts when downcasting -2^${bits - 1} - 2 (${minValue.subn(2)})`, async function () { - await expectRevertCustomError( - this.safeCast[`$toInt${bits}`](minValue.subn(2)), - `SafeCastOverflowedIntDowncast`, - [bits, minValue.subn(2)], - ); + it(`reverts when downcasting -2^${bits - 1n} - 2 (${minValue - 2n})`, async function () { + await expect(this.mock[`$toInt${bits}`](minValue - 2n)) + .to.be.revertedWithCustomError(this.mock, 'SafeCastOverflowedIntDowncast') + .withArgs(bits, minValue - 2n); }); - it(`reverts when downcasting 2^${bits - 1} (${maxValue.addn(1)})`, async function () { - await expectRevertCustomError( - this.safeCast[`$toInt${bits}`](maxValue.addn(1)), - `SafeCastOverflowedIntDowncast`, - [bits, maxValue.addn(1)], - ); + it(`reverts when downcasting 2^${bits - 1n} (${maxValue + 1n})`, async function () { + await expect(this.mock[`$toInt${bits}`](maxValue + 1n)) + .to.be.revertedWithCustomError(this.mock, 'SafeCastOverflowedIntDowncast') + .withArgs(bits, maxValue + 1n); }); - it(`reverts when downcasting 2^${bits - 1} + 1 (${maxValue.addn(2)})`, async function () { - await expectRevertCustomError( - this.safeCast[`$toInt${bits}`](maxValue.addn(2)), - `SafeCastOverflowedIntDowncast`, - [bits, maxValue.addn(2)], - ); + it(`reverts when downcasting 2^${bits - 1n} + 1 (${maxValue + 2n})`, async function () { + await expect(this.mock[`$toInt${bits}`](maxValue + 2n)) + .to.be.revertedWithCustomError(this.mock, 'SafeCastOverflowedIntDowncast') + .withArgs(bits, maxValue + 2n); }); }); } - range(8, 256, 8).forEach(bits => testToInt(bits)); - describe('toInt256', () => { - const maxUint256 = new BN('2').pow(new BN(256)).subn(1); - const maxInt256 = new BN('2').pow(new BN(255)).subn(1); - it('casts 0', async function () { - expect(await this.safeCast.$toInt256(0)).to.be.bignumber.equal('0'); + expect(await this.mock.$toInt256(0)).is.equal(0n); }); it('casts 1', async function () { - expect(await this.safeCast.$toInt256(1)).to.be.bignumber.equal('1'); + expect(await this.mock.$toInt256(1)).is.equal(1n); }); - it(`casts INT256_MAX (${maxInt256})`, async function () { - expect(await this.safeCast.$toInt256(maxInt256)).to.be.bignumber.equal(maxInt256); + it(`casts INT256_MAX (${ethers.MaxInt256})`, async function () { + expect(await this.mock.$toInt256(ethers.MaxInt256)).is.equal(ethers.MaxInt256); }); - it(`reverts when casting INT256_MAX + 1 (${maxInt256.addn(1)})`, async function () { - await expectRevertCustomError(this.safeCast.$toInt256(maxInt256.addn(1)), 'SafeCastOverflowedUintToInt', [ - maxInt256.addn(1), - ]); + it(`reverts when casting INT256_MAX + 1 (${ethers.MaxInt256 + 1n})`, async function () { + await expect(this.mock.$toInt256(ethers.MaxInt256 + 1n)) + .to.be.revertedWithCustomError(this.mock, 'SafeCastOverflowedUintToInt') + .withArgs(ethers.MaxInt256 + 1n); }); - it(`reverts when casting UINT256_MAX (${maxUint256})`, async function () { - await expectRevertCustomError(this.safeCast.$toInt256(maxUint256), 'SafeCastOverflowedUintToInt', [maxUint256]); + it(`reverts when casting UINT256_MAX (${ethers.MaxUint256})`, async function () { + await expect(this.mock.$toInt256(ethers.MaxUint256)) + .to.be.revertedWithCustomError(this.mock, 'SafeCastOverflowedUintToInt') + .withArgs(ethers.MaxUint256); }); }); }); diff --git a/test/utils/math/SignedMath.test.js b/test/utils/math/SignedMath.test.js index c014e22ba9d..051a552cf9e 100644 --- a/test/utils/math/SignedMath.test.js +++ b/test/utils/math/SignedMath.test.js @@ -1,94 +1,51 @@ -const { BN, constants } = require('@openzeppelin/test-helpers'); +const { ethers } = require('hardhat'); const { expect } = require('chai'); -const { MIN_INT256, MAX_INT256 } = constants; +const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +const { min, max } = require('../../helpers/math'); -const SignedMath = artifacts.require('$SignedMath'); +async function testCommutative(fn, lhs, rhs, expected, ...extra) { + expect(await fn(lhs, rhs, ...extra)).to.deep.equal(expected); + expect(await fn(rhs, lhs, ...extra)).to.deep.equal(expected); +} -contract('SignedMath', function () { - const min = new BN('-1234'); - const max = new BN('5678'); +async function fixture() { + const mock = await ethers.deployContract('$SignedMath'); + return { mock }; +} +contract('SignedMath', function () { beforeEach(async function () { - this.math = await SignedMath.new(); + Object.assign(this, await loadFixture(fixture)); }); describe('max', function () { - it('is correctly detected in first argument position', async function () { - expect(await this.math.$max(max, min)).to.be.bignumber.equal(max); - }); - - it('is correctly detected in second argument position', async function () { - expect(await this.math.$max(min, max)).to.be.bignumber.equal(max); + it('is correctly detected in both position', async function () { + testCommutative(this.mock.$min, -1234n, 5678n, [max(-1234n, 5678n)]); }); }); describe('min', function () { - it('is correctly detected in first argument position', async function () { - expect(await this.math.$min(min, max)).to.be.bignumber.equal(min); - }); - - it('is correctly detected in second argument position', async function () { - expect(await this.math.$min(max, min)).to.be.bignumber.equal(min); + it('is correctly detected in both position', async function () { + testCommutative(this.mock.$min, -1234n, 5678n, [min(-1234n, 5678n)]); }); }); describe('average', function () { - function bnAverage(a, b) { - return a.add(b).divn(2); - } - it('is correctly calculated with various input', async function () { - const valuesX = [ - new BN('0'), - new BN('3'), - new BN('-3'), - new BN('4'), - new BN('-4'), - new BN('57417'), - new BN('-57417'), - new BN('42304'), - new BN('-42304'), - MIN_INT256, - MAX_INT256, - ]; - - const valuesY = [ - new BN('0'), - new BN('5'), - new BN('-5'), - new BN('2'), - new BN('-2'), - new BN('57417'), - new BN('-57417'), - new BN('42304'), - new BN('-42304'), - MIN_INT256, - MAX_INT256, - ]; - - for (const x of valuesX) { - for (const y of valuesY) { - expect(await this.math.$average(x, y)).to.be.bignumber.equal( - bnAverage(x, y), - `Bad result for average(${x}, ${y})`, - ); + for (const x of [ethers.MinInt256, -57417n, -42304n, -4n, -3n, 0n, 3n, 4n, 42304n, 57417n, ethers.MaxInt256]) { + for (const y of [ethers.MinInt256, -57417n, -42304n, -5n, -2n, 0n, 2n, 5n, 42304n, 57417n, ethers.MaxInt256]) { + expect(await this.mock.$average(x, y)).to.equal((x + y) / 2n); } } }); }); describe('abs', function () { - for (const n of [ - MIN_INT256, - MIN_INT256.addn(1), - new BN('-1'), - new BN('0'), - new BN('1'), - MAX_INT256.subn(1), - MAX_INT256, - ]) { + const abs = x => (x < 0n ? -x : x); + + for (const n of [ethers.MinInt256, ethers.MinInt256 + 1n, -1n, 0n, 1n, ethers.MaxInt256 - 1n, ethers.MaxInt256]) { it(`correctly computes the absolute value of ${n}`, async function () { - expect(await this.math.$abs(n)).to.be.bignumber.equal(n.abs()); + expect(await this.mock.$abs(n)).to.equal(abs(n)); }); } }); From 2a1841187ac486861765ec0bf5c5f3498f9ad2b0 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 29 Nov 2023 15:47:01 +0100 Subject: [PATCH 02/10] remove duplicate helpers --- test/helpers/iterate.js | 2 ++ test/helpers/math.js | 16 +++++++--------- test/metatx/ERC2771Forwarder.test.js | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/helpers/iterate.js b/test/helpers/iterate.js index 7f6e0e6780c..9b50737bd1e 100644 --- a/test/helpers/iterate.js +++ b/test/helpers/iterate.js @@ -4,6 +4,7 @@ const mapValues = (obj, fn) => Object.fromEntries(Object.entries(obj).map(([k, v // Array of number or bigint const max = (...values) => values.slice(1).reduce((x, y) => (x > y ? x : y), values[0]); const min = (...values) => values.slice(1).reduce((x, y) => (x < y ? x : y), values[0]); +const sum = (...values) => values.slice(1).reduce((x, y) => x + y, values[0]); // Cartesian product of a list of arrays const product = (...arrays) => arrays.reduce((a, b) => a.flatMap(ai => b.map(bi => [...ai, bi])), [[]]); @@ -12,5 +13,6 @@ module.exports = { mapValues, max, min, + sum, product, }; diff --git a/test/helpers/math.js b/test/helpers/math.js index 134f8b04509..9ba45be2cd8 100644 --- a/test/helpers/math.js +++ b/test/helpers/math.js @@ -1,12 +1,10 @@ +const { min, max, sum } = require('./iterate'); + module.exports = { - // sum of integer / bignumber - sum: (...args) => args.reduce((acc, n) => acc + n, 0), - bigintSum: (...args) => args.reduce((acc, n) => acc + n, 0n), + // re-export min, max & sum of integer / bignumber + min, + max, + sum, + // deprecated: BN version of sum BNsum: (...args) => args.reduce((acc, n) => acc.add(n), web3.utils.toBN(0)), - // min of integer / bignumber - min: (...args) => args.slice(1).reduce((x, y) => (x < y ? x : y), args[0]), - BNmin: (...args) => args.slice(1).reduce((x, y) => (x.lt(y) ? x : y), args[0]), - // max of integer / bignumber - max: (...args) => args.slice(1).reduce((x, y) => (x > y ? x : y), args[0]), - BNmax: (...args) => args.slice(1).reduce((x, y) => (x.gt(y) ? x : y), args[0]), }; diff --git a/test/metatx/ERC2771Forwarder.test.js b/test/metatx/ERC2771Forwarder.test.js index a665471f358..e0d1090c4bf 100644 --- a/test/metatx/ERC2771Forwarder.test.js +++ b/test/metatx/ERC2771Forwarder.test.js @@ -4,7 +4,7 @@ const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { getDomain } = require('../helpers/eip712'); const { bigint: time } = require('../helpers/time'); -const { bigintSum: sum } = require('../helpers/math'); +const { sum } = require('../helpers/math'); async function fixture() { const [sender, refundReceiver, another, ...accounts] = await ethers.getSigners(); From b3198c53f8bd10f56520026e4445088ddef64a58 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 29 Nov 2023 15:48:33 +0100 Subject: [PATCH 03/10] up --- test/helpers/iterate.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/helpers/iterate.js b/test/helpers/iterate.js index 9b50737bd1e..536cc93886f 100644 --- a/test/helpers/iterate.js +++ b/test/helpers/iterate.js @@ -2,9 +2,9 @@ const mapValues = (obj, fn) => Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, fn(v)])); // Array of number or bigint -const max = (...values) => values.slice(1).reduce((x, y) => (x > y ? x : y), values[0]); -const min = (...values) => values.slice(1).reduce((x, y) => (x < y ? x : y), values[0]); -const sum = (...values) => values.slice(1).reduce((x, y) => x + y, values[0]); +const max = (...values) => values.slice(1).reduce((x, y) => (x > y ? x : y), values.at(0)); +const min = (...values) => values.slice(1).reduce((x, y) => (x < y ? x : y), values.at(0)); +const sum = (...values) => values.slice(1).reduce((x, y) => x + y, values.at(0)); // Cartesian product of a list of arrays const product = (...arrays) => arrays.reduce((a, b) => a.flatMap(ai => b.map(bi => [...ai, bi])), [[]]); From 477871589d05e45e2b3246f324ea37cc10417c08 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 30 Nov 2023 22:32:16 +0100 Subject: [PATCH 04/10] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ernesto GarcĂ­a --- test/utils/math/Math.test.js | 10 +++++----- test/utils/math/SafeCast.test.js | 6 +++--- test/utils/math/SignedMath.test.js | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/utils/math/Math.test.js b/test/utils/math/Math.test.js index 1bbd1b94db7..585938d5ad7 100644 --- a/test/utils/math/Math.test.js +++ b/test/utils/math/Math.test.js @@ -108,7 +108,7 @@ describe('Math', function () { }); describe('tryMod', function () { - describe('modulos correctly', async function () { + describe('modulos correctly', function () { it('when the dividend is smaller than the divisor', async function () { const a = 284n; const b = 5678n; @@ -143,13 +143,13 @@ describe('Math', function () { describe('max', function () { it('is correctly detected in both position', async function () { - testCommutative(this.mock.$max, 1234n, 5678n, [max(1234n, 5678n)]); + await testCommutative(this.mock.$max, 1234n, 5678n, [max(1234n, 5678n)]); }); }); describe('min', function () { it('is correctly detected in both position', async function () { - testCommutative(this.mock.$min, 1234n, 5678n, [min(1234n, 5678n)]); + await testCommutative(this.mock.$min, 1234n, 5678n, [min(1234n, 5678n)]); }); }); @@ -240,7 +240,7 @@ describe('Math', function () { ); }); - describe('does round down', async function () { + describe('does round down', function () { it('small values', async function () { for (const rounding of RoundingDown) { expect(await this.mock.$mulDiv(3n, 4n, 5n, rounding)).to.equal(2n); @@ -269,7 +269,7 @@ describe('Math', function () { }); }); - describe('does round up', async function () { + describe('does round up', function () { it('small values', async function () { for (const rounding of RoundingUp) { expect(await this.mock.$mulDiv(3n, 4n, 5n, rounding)).to.equal(3n); diff --git a/test/utils/math/SafeCast.test.js b/test/utils/math/SafeCast.test.js index c081524c137..dd04f75ba1a 100644 --- a/test/utils/math/SafeCast.test.js +++ b/test/utils/math/SafeCast.test.js @@ -8,7 +8,7 @@ async function fixture() { return { mock }; } -contract('SafeCast', async function () { +contract('SafeCast', function () { beforeEach(async function () { Object.assign(this, await loadFixture(fixture)); }); @@ -58,13 +58,13 @@ contract('SafeCast', async function () { it('reverts when casting -1', async function () { await expect(this.mock.$toUint256(-1n)) - .to.be.revertedWithCustomError(this.mock, `SafeCastOverflowedIntToUint`) + .to.be.revertedWithCustomError(this.mock, 'SafeCastOverflowedIntToUint') .withArgs(-1n); }); it(`reverts when casting INT256_MIN (${ethers.MinInt256})`, async function () { await expect(this.mock.$toUint256(ethers.MinInt256)) - .to.be.revertedWithCustomError(this.mock, `SafeCastOverflowedIntToUint`) + .to.be.revertedWithCustomError(this.mock, 'SafeCastOverflowedIntToUint') .withArgs(ethers.MinInt256); }); }); diff --git a/test/utils/math/SignedMath.test.js b/test/utils/math/SignedMath.test.js index 051a552cf9e..8bdf6265696 100644 --- a/test/utils/math/SignedMath.test.js +++ b/test/utils/math/SignedMath.test.js @@ -20,13 +20,13 @@ contract('SignedMath', function () { describe('max', function () { it('is correctly detected in both position', async function () { - testCommutative(this.mock.$min, -1234n, 5678n, [max(-1234n, 5678n)]); + await testCommutative(this.mock.$min, -1234n, 5678n, [max(-1234n, 5678n)]); }); }); describe('min', function () { it('is correctly detected in both position', async function () { - testCommutative(this.mock.$min, -1234n, 5678n, [min(-1234n, 5678n)]); + await testCommutative(this.mock.$min, -1234n, 5678n, [min(-1234n, 5678n)]); }); }); From a2972bd0a83be01b226c4f48392d77abbc359b86 Mon Sep 17 00:00:00 2001 From: ernestognw Date: Thu, 30 Nov 2023 15:32:29 -0600 Subject: [PATCH 05/10] Fix tests --- test/token/ERC721/extensions/ERC721Consecutive.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/token/ERC721/extensions/ERC721Consecutive.test.js b/test/token/ERC721/extensions/ERC721Consecutive.test.js index d9e33aff29b..e4ee3196d44 100644 --- a/test/token/ERC721/extensions/ERC721Consecutive.test.js +++ b/test/token/ERC721/extensions/ERC721Consecutive.test.js @@ -70,7 +70,8 @@ contract('ERC721Consecutive', function (accounts) { it('balance & voting power are set', async function () { for (const account of accounts) { - const balance = sum(...batches.filter(({ receiver }) => receiver === account).map(({ amount }) => amount)); + const balance = + sum(...batches.filter(({ receiver }) => receiver === account).map(({ amount }) => amount)) ?? 0; expect(await this.token.balanceOf(account)).to.be.bignumber.equal(web3.utils.toBN(balance)); From 40433b44e2f692b5bc76fd0b08e9a7ddaa9ec526 Mon Sep 17 00:00:00 2001 From: ernestognw Date: Thu, 30 Nov 2023 17:41:30 -0600 Subject: [PATCH 06/10] Do fix tests --- test/utils/math/Math.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/utils/math/Math.test.js b/test/utils/math/Math.test.js index 585938d5ad7..6e4fa3b9c5a 100644 --- a/test/utils/math/Math.test.js +++ b/test/utils/math/Math.test.js @@ -143,13 +143,13 @@ describe('Math', function () { describe('max', function () { it('is correctly detected in both position', async function () { - await testCommutative(this.mock.$max, 1234n, 5678n, [max(1234n, 5678n)]); + await testCommutative(this.mock.$max, 1234n, 5678n, max(1234n, 5678n)); }); }); describe('min', function () { it('is correctly detected in both position', async function () { - await testCommutative(this.mock.$min, 1234n, 5678n, [min(1234n, 5678n)]); + await testCommutative(this.mock.$min, 1234n, 5678n, min(1234n, 5678n)); }); }); From c04b51068436601ba06959dff50e3df49014fa9f Mon Sep 17 00:00:00 2001 From: ernestognw Date: Thu, 30 Nov 2023 18:07:38 -0600 Subject: [PATCH 07/10] Fix SignedMath tests --- test/utils/math/SignedMath.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/utils/math/SignedMath.test.js b/test/utils/math/SignedMath.test.js index 8bdf6265696..253e7235752 100644 --- a/test/utils/math/SignedMath.test.js +++ b/test/utils/math/SignedMath.test.js @@ -20,13 +20,13 @@ contract('SignedMath', function () { describe('max', function () { it('is correctly detected in both position', async function () { - await testCommutative(this.mock.$min, -1234n, 5678n, [max(-1234n, 5678n)]); + await testCommutative(this.mock.$max, -1234n, 5678n, max(-1234n, 5678n)); }); }); describe('min', function () { it('is correctly detected in both position', async function () { - await testCommutative(this.mock.$min, -1234n, 5678n, [min(-1234n, 5678n)]); + await testCommutative(this.mock.$min, -1234n, 5678n, min(-1234n, 5678n)); }); }); From b0adb67be4e8887c84dc9d0da61b290c39d3c1cf Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 1 Dec 2023 14:40:08 +0100 Subject: [PATCH 08/10] move min/max/sum to helpers/math.js --- test/helpers/iterate.js | 8 -------- test/helpers/math.js | 7 +++++-- test/utils/types/Time.test.js | 3 ++- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/test/helpers/iterate.js b/test/helpers/iterate.js index 536cc93886f..2a84dfbebdc 100644 --- a/test/helpers/iterate.js +++ b/test/helpers/iterate.js @@ -1,18 +1,10 @@ // Map values in an object const mapValues = (obj, fn) => Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, fn(v)])); -// Array of number or bigint -const max = (...values) => values.slice(1).reduce((x, y) => (x > y ? x : y), values.at(0)); -const min = (...values) => values.slice(1).reduce((x, y) => (x < y ? x : y), values.at(0)); -const sum = (...values) => values.slice(1).reduce((x, y) => x + y, values.at(0)); - // Cartesian product of a list of arrays const product = (...arrays) => arrays.reduce((a, b) => a.flatMap(ai => b.map(bi => [...ai, bi])), [[]]); module.exports = { mapValues, - max, - min, - sum, product, }; diff --git a/test/helpers/math.js b/test/helpers/math.js index 9ba45be2cd8..55352c14114 100644 --- a/test/helpers/math.js +++ b/test/helpers/math.js @@ -1,4 +1,7 @@ -const { min, max, sum } = require('./iterate'); +// Array of number or bigint +const max = (...values) => values.slice(1).reduce((x, y) => (x > y ? x : y), values.at(0)); +const min = (...values) => values.slice(1).reduce((x, y) => (x < y ? x : y), values.at(0)); +const sum = (...values) => values.slice(1).reduce((x, y) => x + y, values.at(0)); module.exports = { // re-export min, max & sum of integer / bignumber @@ -6,5 +9,5 @@ module.exports = { max, sum, // deprecated: BN version of sum - BNsum: (...args) => args.reduce((acc, n) => acc.add(n), web3.utils.toBN(0)), + BNsum: (...args) => args.slice(1).reduce((x, y) => x.add(y), x.at(0)), }; diff --git a/test/utils/types/Time.test.js b/test/utils/types/Time.test.js index 614911738d2..d30daffc2c4 100644 --- a/test/utils/types/Time.test.js +++ b/test/utils/types/Time.test.js @@ -2,7 +2,8 @@ require('@openzeppelin/test-helpers'); const { expect } = require('chai'); const { clock } = require('../../helpers/time'); -const { product, max } = require('../../helpers/iterate'); +const { product } = require('../../helpers/iterate'); +const { max } = require('../../helpers/math'); const Time = artifacts.require('$Time'); From 1f7e47f36813475c5bb82768f1078cc52f69b45f Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 1 Dec 2023 15:02:32 +0100 Subject: [PATCH 09/10] fix helper --- test/helpers/math.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/helpers/math.js b/test/helpers/math.js index 55352c14114..708990519a3 100644 --- a/test/helpers/math.js +++ b/test/helpers/math.js @@ -9,5 +9,5 @@ module.exports = { max, sum, // deprecated: BN version of sum - BNsum: (...args) => args.slice(1).reduce((x, y) => x.add(y), x.at(0)), + BNsum: (...args) => args.slice(1).reduce((x, y) => x.add(y), args.at(0)), }; From db4ba2aa0e41042b84dae485ac3ac9d594397003 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 1 Dec 2023 15:03:10 +0100 Subject: [PATCH 10/10] Migrate utils/types/time helper --- test/utils/types/Time.test.js | 98 +++++++++++++++++------------------ 1 file changed, 47 insertions(+), 51 deletions(-) diff --git a/test/utils/types/Time.test.js b/test/utils/types/Time.test.js index d30daffc2c4..c55a769f985 100644 --- a/test/utils/types/Time.test.js +++ b/test/utils/types/Time.test.js @@ -1,24 +1,22 @@ -require('@openzeppelin/test-helpers'); - +const { ethers } = require('hardhat'); const { expect } = require('chai'); -const { clock } = require('../../helpers/time'); +const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +const { + bigint: { clock }, +} = require('../../helpers/time'); + const { product } = require('../../helpers/iterate'); const { max } = require('../../helpers/math'); -const Time = artifacts.require('$Time'); - const MAX_UINT32 = 1n << (32n - 1n); const MAX_UINT48 = 1n << (48n - 1n); const SOME_VALUES = [0n, 1n, 2n, 15n, 16n, 17n, 42n]; const asUint = (value, size) => { - if (typeof value != 'bigint') { - value = BigInt(value); - } - // chai does not support bigint :/ - if (value < 0 || value >= 1n << BigInt(size)) { - throw new Error(`value is not a valid uint${size}`); - } + value = ethers.toBigInt(value); + size = ethers.toBigInt(size); + expect(value).to.be.greaterThanOrEqual(0n, `value is not a valid uint${size}`); + expect(value).to.be.lessThan(1n << size, `value is not a valid uint${size}`); return value; }; @@ -40,18 +38,23 @@ const effectSamplesForTimepoint = timepoint => [ MAX_UINT48, ]; +async function fixture() { + const mock = await ethers.deployContract('$Time'); + return { mock }; +} + contract('Time', function () { beforeEach(async function () { - this.mock = await Time.new(); + Object.assign(this, await loadFixture(fixture)); }); describe('clocks', function () { it('timestamp', async function () { - expect(await this.mock.$timestamp()).to.be.bignumber.equal(web3.utils.toBN(await clock.timestamp())); + expect(await this.mock.$timestamp()).to.equal(await clock.timestamp()); }); it('block number', async function () { - expect(await this.mock.$blockNumber()).to.be.bignumber.equal(web3.utils.toBN(await clock.blocknumber())); + expect(await this.mock.$blockNumber()).to.equal(await clock.blocknumber()); }); }); @@ -63,28 +66,28 @@ contract('Time', function () { const delay = 1272825341158973505578n; it('pack', async function () { - const packed = await this.mock.$pack(valueBefore, valueAfter, effect); - expect(packed).to.be.bignumber.equal(delay.toString()); - - const packed2 = packDelay({ valueBefore, valueAfter, effect }); - expect(packed2).to.be.equal(delay); + expect(await this.mock.$pack(valueBefore, valueAfter, effect)).to.equal(delay); + expect(packDelay({ valueBefore, valueAfter, effect })).to.equal(delay); }); it('unpack', async function () { - const unpacked = await this.mock.$unpack(delay); - expect(unpacked[0]).to.be.bignumber.equal(valueBefore.toString()); - expect(unpacked[1]).to.be.bignumber.equal(valueAfter.toString()); - expect(unpacked[2]).to.be.bignumber.equal(effect.toString()); + expect(await this.mock.$unpack(delay)).to.deep.equal([valueBefore, valueAfter, effect]); - const unpacked2 = unpackDelay(delay); - expect(unpacked2).to.be.deep.equal({ valueBefore, valueAfter, effect }); + expect(unpackDelay(delay)).to.deep.equal({ + valueBefore, + valueAfter, + effect, + }); }); }); it('toDelay', async function () { for (const value of [...SOME_VALUES, MAX_UINT32]) { - const delay = await this.mock.$toDelay(value).then(unpackDelay); - expect(delay).to.be.deep.equal({ valueBefore: 0n, valueAfter: value, effect: 0n }); + expect(await this.mock.$toDelay(value).then(unpackDelay)).to.deep.equal({ + valueBefore: 0n, + valueAfter: value, + effect: 0n, + }); } }); @@ -95,15 +98,14 @@ contract('Time', function () { for (const effect of effectSamplesForTimepoint(timepoint)) { const isPast = effect <= timepoint; - const delay = packDelay({ valueBefore, valueAfter, effect }); - expect(await this.mock.$get(delay)).to.be.bignumber.equal(String(isPast ? valueAfter : valueBefore)); - - const result = await this.mock.$getFull(delay); - expect(result[0]).to.be.bignumber.equal(String(isPast ? valueAfter : valueBefore)); - expect(result[1]).to.be.bignumber.equal(String(isPast ? 0n : valueAfter)); - expect(result[2]).to.be.bignumber.equal(String(isPast ? 0n : effect)); + expect(await this.mock.$get(delay)).to.equal(isPast ? valueAfter : valueBefore); + expect(await this.mock.$getFull(delay)).to.deep.equal([ + isPast ? valueAfter : valueBefore, + isPast ? 0n : valueAfter, + isPast ? 0n : effect, + ]); } }); @@ -119,22 +121,16 @@ contract('Time', function () { const expectedvalueBefore = isPast ? valueAfter : valueBefore; const expectedSetback = max(minSetback, expectedvalueBefore - newvalueAfter, 0n); - const result = await this.mock.$withUpdate( - packDelay({ valueBefore, valueAfter, effect }), - newvalueAfter, - minSetback, - ); - - expect(result[0]).to.be.bignumber.equal( - String( - packDelay({ - valueBefore: expectedvalueBefore, - valueAfter: newvalueAfter, - effect: timepoint + expectedSetback, - }), - ), - ); - expect(result[1]).to.be.bignumber.equal(String(timepoint + expectedSetback)); + expect( + await this.mock.$withUpdate(packDelay({ valueBefore, valueAfter, effect }), newvalueAfter, minSetback), + ).to.deep.equal([ + packDelay({ + valueBefore: expectedvalueBefore, + valueAfter: newvalueAfter, + effect: timepoint + expectedSetback, + }), + timepoint + expectedSetback, + ]); } }); });