-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: added semver comparision lib (#479)
* feat: added semver comparision lib * docs: fix wrong NatSpec Co-authored-by: Mathias Scherer <mathias@aragon.org> * fix: remove redundant await and async * style: move helpers to the file end --------- Co-authored-by: Mathias Scherer <mathias@aragon.org>
- Loading branch information
1 parent
ed1810f
commit d404a2b
Showing
5 changed files
with
331 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
packages/contracts/src/test/utils/VersionComparisonLibTest.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-or-later | ||
|
||
pragma solidity 0.8.17; | ||
|
||
import {VersionComparisonLib} from "../../utils/protocol/VersionComparisonLib.sol"; | ||
|
||
contract VersionComparisonLibTest { | ||
using VersionComparisonLib for uint8[3]; | ||
|
||
function eq(uint8[3] memory lhs, uint8[3] memory rhs) public pure returns (bool) { | ||
return lhs.eq(rhs); | ||
} | ||
|
||
function neq(uint8[3] memory lhs, uint8[3] memory rhs) public pure returns (bool) { | ||
return lhs.neq(rhs); | ||
} | ||
|
||
function lt(uint8[3] memory lhs, uint8[3] memory rhs) public pure returns (bool) { | ||
return lhs.lt(rhs); | ||
} | ||
|
||
function lte(uint8[3] memory lhs, uint8[3] memory rhs) public pure returns (bool) { | ||
return lhs.lte(rhs); | ||
} | ||
|
||
function gt(uint8[3] memory lhs, uint8[3] memory rhs) public pure returns (bool) { | ||
return lhs.gt(rhs); | ||
} | ||
|
||
function gte(uint8[3] memory lhs, uint8[3] memory rhs) public pure returns (bool) { | ||
return lhs.gte(rhs); | ||
} | ||
} |
97 changes: 97 additions & 0 deletions
97
packages/contracts/src/utils/protocol/VersionComparisonLib.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-or-later | ||
|
||
pragma solidity ^0.8.8; | ||
|
||
library VersionComparisonLib { | ||
/// @notice Equality comparator for two semantic version numbers. | ||
/// @param lhs The left-hand side semantic version number. | ||
/// @param rhs The right-hand side semantic version number. | ||
/// @return Whether the two numbers are equal or not. | ||
function eq(uint8[3] memory lhs, uint8[3] memory rhs) internal pure returns (bool) { | ||
if (lhs[0] != rhs[0]) return false; | ||
|
||
if (lhs[1] != rhs[1]) return false; | ||
|
||
if (lhs[2] != rhs[2]) return false; | ||
|
||
return true; | ||
} | ||
|
||
/// @notice Inequality comparator for two semantic version numbers. | ||
/// @param lhs The left-hand side semantic version number. | ||
/// @param rhs The right-hand side semantic version number. | ||
/// @return Whether the two numbers are inequal or not. | ||
function neq(uint8[3] memory lhs, uint8[3] memory rhs) internal pure returns (bool) { | ||
if (lhs[0] != rhs[0]) return true; | ||
|
||
if (lhs[1] != rhs[1]) return true; | ||
|
||
if (lhs[2] != rhs[2]) return true; | ||
|
||
return false; | ||
} | ||
|
||
/// @notice Less than comparator for two semantic version numbers. | ||
/// @param lhs The left-hand side semantic version number. | ||
/// @param rhs The right-hand side semantic version number. | ||
/// @return Whether the first number is less than the second number or not. | ||
function lt(uint8[3] memory lhs, uint8[3] memory rhs) internal pure returns (bool) { | ||
if (lhs[0] < rhs[0]) return true; | ||
if (lhs[0] > rhs[0]) return false; | ||
|
||
if (lhs[1] < rhs[1]) return true; | ||
if (lhs[1] > rhs[1]) return false; | ||
|
||
if (lhs[2] < rhs[2]) return true; | ||
|
||
return false; | ||
} | ||
|
||
/// @notice Less than or equal to comparator for two semantic version numbers. | ||
/// @param lhs The left-hand side semantic version number. | ||
/// @param rhs The right-hand side semantic version number. | ||
/// @return Whether the first number is less than or equal to the second number or not. | ||
function lte(uint8[3] memory lhs, uint8[3] memory rhs) internal pure returns (bool) { | ||
if (lhs[0] < rhs[0]) return true; | ||
if (lhs[0] > rhs[0]) return false; | ||
|
||
if (lhs[1] < rhs[1]) return true; | ||
if (lhs[1] > rhs[1]) return false; | ||
|
||
if (lhs[2] <= rhs[2]) return true; | ||
|
||
return false; | ||
} | ||
|
||
/// @notice Greater than comparator for two semantic version numbers. | ||
/// @param lhs The left-hand side semantic version number. | ||
/// @param rhs The right-hand side semantic version number. | ||
/// @return Whether the first number is greater than the second number or not. | ||
function gt(uint8[3] memory lhs, uint8[3] memory rhs) internal pure returns (bool) { | ||
if (lhs[0] > rhs[0]) return true; | ||
if (lhs[0] < rhs[0]) return false; | ||
|
||
if (lhs[1] > rhs[1]) return true; | ||
if (lhs[1] < rhs[1]) return false; | ||
|
||
if (lhs[2] > rhs[2]) return true; | ||
|
||
return false; | ||
} | ||
|
||
/// @notice Greater than or equal to comparator for two semantic version numbers. | ||
/// @param lhs The left-hand side semantic version number. | ||
/// @param rhs The right-hand side semantic version number. | ||
/// @return Whether the first number is greater than or equal to the second number or not. | ||
function gte(uint8[3] memory lhs, uint8[3] memory rhs) internal pure returns (bool) { | ||
if (lhs[0] > rhs[0]) return true; | ||
if (lhs[0] < rhs[0]) return false; | ||
|
||
if (lhs[1] > rhs[1]) return true; | ||
if (lhs[1] < rhs[1]) return false; | ||
|
||
if (lhs[2] >= rhs[2]) return true; | ||
|
||
return false; | ||
} | ||
} |
196 changes: 196 additions & 0 deletions
196
packages/contracts/test/utils/version-comparison-lib.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
import {expect} from 'chai'; | ||
import {ethers} from 'hardhat'; | ||
|
||
import { | ||
VersionComparisonLibTest, | ||
VersionComparisonLibTest__factory, | ||
} from '../../typechain'; | ||
|
||
type SemVer = [number, number, number]; | ||
|
||
describe('VersionComparisonLib', function () { | ||
let cmp: VersionComparisonLibTest; | ||
|
||
before(async () => { | ||
const signers = await ethers.getSigners(); | ||
cmp = await new VersionComparisonLibTest__factory(signers[0]).deploy(); | ||
}); | ||
|
||
describe('eq', async () => { | ||
function eq(lhs: SemVer, rhs: SemVer): Promise<boolean> { | ||
return cmp.eq(lhs, rhs); | ||
} | ||
|
||
it('returns true if lhs equals rhs', async () => { | ||
await eqChecks(eq, true); | ||
}); | ||
|
||
it('returns false if lhs does not equal rhs', async () => { | ||
await ltChecks(eq, false); | ||
await gtChecks(eq, false); | ||
}); | ||
}); | ||
|
||
describe('neq', async () => { | ||
function neq(lhs: SemVer, rhs: SemVer): Promise<boolean> { | ||
return cmp.neq(lhs, rhs); | ||
} | ||
|
||
it('returns true if lhs does not equal rhs', async () => { | ||
await ltChecks(neq, true); | ||
await gtChecks(neq, true); | ||
}); | ||
|
||
it('returns false if lhs equals rhs', async () => { | ||
await eqChecks(neq, false); | ||
}); | ||
}); | ||
|
||
describe('lt', async () => { | ||
function lt(lhs: SemVer, rhs: SemVer): Promise<boolean> { | ||
return cmp.lt(lhs, rhs); | ||
} | ||
|
||
it('returns true if lhs is less than rhs', async () => { | ||
await ltChecks(lt, true); | ||
}); | ||
|
||
it('returns false if lhs is not less than rhs', async () => { | ||
await gtChecks(lt, false); | ||
await eqChecks(lt, false); | ||
}); | ||
}); | ||
|
||
describe('lte', async () => { | ||
function lte(lhs: SemVer, rhs: SemVer): Promise<boolean> { | ||
return cmp.lte(lhs, rhs); | ||
} | ||
|
||
it('returns true if lhs is less than or equal to rhs', async () => { | ||
await ltChecks(lte, true); | ||
await eqChecks(lte, true); | ||
}); | ||
|
||
it('returns false if lhs is not less than or equal to rhs', async () => { | ||
await gtChecks(lte, false); | ||
}); | ||
}); | ||
|
||
describe('gt', async () => { | ||
function gt(lhs: SemVer, rhs: SemVer): Promise<boolean> { | ||
return cmp.gt(lhs, rhs); | ||
} | ||
|
||
it('returns true if lhs is greater than rhs', async () => { | ||
await gtChecks(gt, true); | ||
}); | ||
|
||
it('returns false if lhs is not greater than rhs', async () => { | ||
await ltChecks(gt, false); | ||
await eqChecks(gt, false); | ||
}); | ||
}); | ||
|
||
describe('gte', async () => { | ||
function gte(lhs: SemVer, rhs: SemVer): Promise<boolean> { | ||
return cmp.gte(lhs, rhs); | ||
} | ||
|
||
it('returns true if lhs is greater than or equal to rhs', async () => { | ||
await gtChecks(gte, true); | ||
await eqChecks(gte, true); | ||
}); | ||
|
||
it('returns false if lhs is not greater than or equal to rhs', async () => { | ||
await ltChecks(gte, false); | ||
}); | ||
}); | ||
}); | ||
|
||
async function eqChecks( | ||
func: (lhs: SemVer, rhs: SemVer) => Promise<boolean>, | ||
expected: boolean | ||
) { | ||
const results: boolean[] = await Promise.all([ | ||
func([1, 1, 1], [1, 1, 1]), | ||
// | ||
func([0, 1, 1], [0, 1, 1]), | ||
func([1, 0, 1], [1, 0, 1]), | ||
func([1, 1, 0], [1, 1, 0]), | ||
// | ||
func([1, 0, 0], [1, 0, 0]), | ||
func([0, 1, 0], [0, 1, 0]), | ||
func([0, 0, 1], [0, 0, 1]), | ||
// | ||
func([0, 0, 0], [0, 0, 0]), | ||
]); | ||
|
||
// Check that all results match the expected value | ||
expect(results.every(v => v === expected)).to.be.true; | ||
} | ||
|
||
async function ltChecks( | ||
func: (lhs: SemVer, rhs: SemVer) => Promise<boolean>, | ||
expected: boolean | ||
) { | ||
const results: boolean[] = await Promise.all([ | ||
func([1, 1, 1], [2, 1, 1]), | ||
func([1, 1, 1], [1, 2, 1]), | ||
func([1, 1, 1], [1, 1, 2]), | ||
// | ||
func([1, 1, 1], [1, 2, 2]), | ||
func([1, 1, 1], [2, 1, 2]), | ||
func([1, 1, 1], [2, 2, 1]), | ||
// | ||
func([1, 1, 1], [2, 2, 2]), | ||
// | ||
func([1, 1, 0], [1, 2, 0]), | ||
func([1, 1, 0], [2, 1, 0]), | ||
// | ||
func([1, 1, 0], [2, 2, 0]), | ||
// | ||
func([0, 1, 1], [0, 1, 2]), | ||
func([0, 1, 1], [0, 2, 1]), | ||
// | ||
func([0, 1, 1], [0, 2, 2]), | ||
// | ||
func([1, 0, 0], [2, 0, 0]), | ||
func([0, 1, 0], [0, 2, 0]), | ||
func([0, 0, 1], [0, 0, 2]), | ||
]); | ||
|
||
// Check that all results match the expected value | ||
expect(results.every(v => v === expected)).to.be.true; | ||
} | ||
|
||
async function gtChecks( | ||
func: (lhs: SemVer, rhs: SemVer) => Promise<boolean>, | ||
expected: boolean | ||
) { | ||
const results: boolean[] = await Promise.all([ | ||
func([2, 1, 1], [1, 1, 1]), | ||
func([1, 2, 1], [1, 1, 1]), | ||
func([1, 1, 2], [1, 1, 1]), | ||
// | ||
func([1, 2, 2], [1, 1, 1]), | ||
func([2, 1, 2], [1, 1, 1]), | ||
func([2, 2, 1], [1, 1, 1]), | ||
// | ||
func([2, 2, 2], [1, 1, 1]), | ||
// | ||
func([1, 2, 0], [1, 1, 0]), | ||
func([2, 1, 0], [1, 1, 0]), | ||
func([2, 2, 0], [1, 1, 0]), | ||
// | ||
func([0, 1, 2], [0, 1, 1]), | ||
func([0, 2, 1], [0, 1, 1]), | ||
func([0, 2, 2], [0, 1, 1]), | ||
// | ||
func([2, 0, 0], [1, 0, 0]), | ||
func([0, 2, 0], [0, 1, 0]), | ||
func([0, 0, 2], [0, 0, 1]), | ||
]); | ||
|
||
// Check that all results match the expected value | ||
expect(results.every(v => v === expected)).to.be.true; | ||
} |