Skip to content

Commit

Permalink
Add permissioned versions of all Business Scenarios (#130)
Browse files Browse the repository at this point in the history
* Update VeriteAccessControl contract to be abstract

* Add missing @types/uuid

* Add extra check with verite during helper

* Add business scenario #1 test for Verite

* Add business scenario 2

* Add Permissioned scenario 3

* Ensure permitted to request withdrawal/redeem as well

* Add permissioned scenario 4

* Fix error message

* Update permissioned scenario test names

* Fix variable name in tests
  • Loading branch information
venables authored Dec 7, 2022
1 parent 6092a07 commit 1cdc15c
Show file tree
Hide file tree
Showing 14 changed files with 1,374 additions and 12 deletions.
4 changes: 4 additions & 0 deletions contracts/Pool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ contract Pool is IPool, ERC20Upgradeable, IBeaconImplementation {
function requestRedeem(uint256 shares)
external
onlyActivatedPool
onlyPermittedLender
onlyLender
onlyCrankedPool
returns (uint256 assets)
Expand All @@ -455,6 +456,7 @@ contract Pool is IPool, ERC20Upgradeable, IBeaconImplementation {
function requestWithdraw(uint256 assets)
external
onlyActivatedPool
onlyPermittedLender
onlyLender
onlyCrankedPool
returns (uint256 shares)
Expand Down Expand Up @@ -508,6 +510,7 @@ contract Pool is IPool, ERC20Upgradeable, IBeaconImplementation {
function cancelRedeemRequest(uint256 shares)
external
onlyActivatedPool
onlyPermittedLender
onlyLender
onlyCrankedPool
returns (uint256 assets)
Expand All @@ -526,6 +529,7 @@ contract Pool is IPool, ERC20Upgradeable, IBeaconImplementation {
function cancelWithdrawRequest(uint256 assets)
external
onlyActivatedPool
onlyPermittedLender
onlyLender
onlyCrankedPool
returns (uint256 shares)
Expand Down
6 changes: 6 additions & 0 deletions contracts/mocks/MockVeriteAccessControl.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.16;

import {VeriteAccessControl} from "../permissioned/VeriteAccessControl.sol";

contract MockVeriteAccessControl is VeriteAccessControl {}
2 changes: 1 addition & 1 deletion contracts/permissioned/VeriteAccessControl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
* Other contracts should inherit this contract to add Verite-specific
* access control logic.
*/
contract VeriteAccessControl is
abstract contract VeriteAccessControl is
IVeriteAccessControl,
EIP712("VerificationRegistry", "1.0")
{
Expand Down
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@nomicfoundation/hardhat-toolbox": "^2.0.0",
"@openzeppelin/contracts-upgradeable": "^4.8.0",
"@openzeppelin/hardhat-upgrades": "^1.21.0",
"@types/uuid": "^9.0.0",
"@typescript-eslint/eslint-plugin": "^5.36.2",
"@typescript-eslint/parser": "^5.36.2",
"eslint": "^8.23.0",
Expand Down
167 changes: 167 additions & 0 deletions test/permissioned/VeriteAccessControl.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import { ethers } from "hardhat";
import { getSignedVerificationResult } from "../support/verite";
import { expect } from "chai";

describe("VeriteAccessControl", () => {
async function deployFixture() {
const [verifier, admin, subject, otherSubject] = await ethers.getSigners();

const VeriteAccessControl = await ethers.getContractFactory(
"MockVeriteAccessControl"
);
const veriteAccessControl = await VeriteAccessControl.deploy();
await veriteAccessControl.deployed();

return {
veriteAccessControl,
verifier,
admin,
subject,
otherSubject
};
}

describe("verify()", () => {
it("succeeds when given a valid verification result from a trusted verifier", async () => {
const { veriteAccessControl, verifier, admin, subject } =
await deployFixture();

// Register the verifier
await veriteAccessControl
.connect(admin)
.addTrustedVerifier(verifier.address);

// Get a signed verification result
const { verificationResult, signature } =
await getSignedVerificationResult(
veriteAccessControl.address,
subject.address,
verifier
);

// Register the schema
await veriteAccessControl
.connect(admin)
.addCredentialSchema(verificationResult.schema);

// Verify the verification result
await expect(
veriteAccessControl
.connect(subject)
.verify(verificationResult, signature)
)
.to.emit(veriteAccessControl, "VerificationResultConfirmed")
.withArgs(subject.address);
});

it("reverts if the verifier is not trusted", async () => {
const { veriteAccessControl, verifier, admin, subject } =
await deployFixture();

// Get a signed verification result
const { verificationResult, signature } =
await getSignedVerificationResult(
veriteAccessControl.address,
subject.address,
verifier
);

// Register the schema
await veriteAccessControl
.connect(admin)
.addCredentialSchema(verificationResult.schema);

await expect(
veriteAccessControl
.connect(subject)
.verify(verificationResult, signature)
).to.be.revertedWith("INVALID_SIGNER");
});

it("reverts if the schema is not valid", async () => {
const { veriteAccessControl, verifier, admin, subject } =
await deployFixture();

// Register the verifier
await veriteAccessControl
.connect(admin)
.addTrustedVerifier(verifier.address);

// Get a signed verification result
const { verificationResult, signature } =
await getSignedVerificationResult(
veriteAccessControl.address,
subject.address,
verifier
);

// Verify the verification result
await expect(
veriteAccessControl
.connect(subject)
.verify(verificationResult, signature)
).to.be.revertedWith("INVALID_CREDENTIAL_SCHEMA");
});

it("reverts if the subject does not match the sender", async () => {
const { veriteAccessControl, verifier, admin, subject, otherSubject } =
await deployFixture();

// Register the verifier
await veriteAccessControl
.connect(admin)
.addTrustedVerifier(verifier.address);

// Get a signed verification result
const { verificationResult, signature } =
await getSignedVerificationResult(
veriteAccessControl.address,
subject.address,
verifier
);

// Register the schema
await veriteAccessControl
.connect(admin)
.addCredentialSchema(verificationResult.schema);

// Verify the verification result
await expect(
veriteAccessControl
.connect(otherSubject)
.verify(verificationResult, signature)
).to.be.revertedWith("SUBJECT_MISMATCH");
});

it("reverts if the result is expired", async () => {
const { veriteAccessControl, verifier, admin, subject } =
await deployFixture();

// Register the verifier
await veriteAccessControl
.connect(admin)
.addTrustedVerifier(verifier.address);

// Get a signed verification result
const { verificationResult, signature } =
await getSignedVerificationResult(
veriteAccessControl.address,
subject.address,
verifier,
{ expiration: 0 }
);

// Register the schema
await veriteAccessControl
.connect(admin)
.addCredentialSchema(verificationResult.schema);

// Verify the verification result
await expect(
veriteAccessControl
.connect(subject)
.verify(verificationResult, signature)
).to.be.revertedWith("VERIFICATION_RESULT_EXPIRED");
});
});
});
2 changes: 1 addition & 1 deletion test/scenarios/business/2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ describe("Business Scenario 2", () => {
expect(await pool.maxWithdraw(lenderA.address)).to.equal(0);
expect(await pool.maxWithdraw(lenderB.address)).to.equal(0);

// +8 days, lenderB requests 300k PT redeption
// +8 days, lenderB requests 300k PT redemption
await advanceToDay(startTime, 8);
await pool.connect(lenderB).requestRedeem(300_000_000_000);

Expand Down
16 changes: 8 additions & 8 deletions test/scenarios/business/3.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe("Business Scenario 3", () => {
}

async function fixtures() {
const [operator, poolManager, lenderA, lenderB, borrower] =
const [operator, poolAdmin, lenderA, lenderB, borrower] =
await ethers.getSigners();
const endTime = (await time.latest()) + 5_184_000; // 60 days.
const poolSettings = {
Expand All @@ -53,7 +53,7 @@ describe("Business Scenario 3", () => {
);
const { pool, serviceConfiguration, poolController } = await deployPool({
operator,
poolAdmin: poolManager,
poolAdmin: poolAdmin,
settings: poolSettings,
liquidityAsset: mockUSDC
});
Expand All @@ -62,7 +62,7 @@ describe("Business Scenario 3", () => {
expect(await serviceConfiguration.firstLossFeeBps()).to.equal(500);

// activate pool
await activatePool(pool, poolManager, mockUSDC);
await activatePool(pool, poolAdmin, mockUSDC);
const startTime = (await pool.activatedAt()).toNumber();

// Mint for lenders
Expand Down Expand Up @@ -95,7 +95,7 @@ describe("Business Scenario 3", () => {
lenderA,
lenderB,
mockUSDC,
poolManager,
poolAdmin,
borrower,
loan
};
Expand All @@ -109,7 +109,7 @@ describe("Business Scenario 3", () => {
lenderA,
lenderB,
mockUSDC,
poolManager,
poolAdmin,
borrower,
loan
} = await loadFixture(fixtures);
Expand All @@ -118,7 +118,7 @@ describe("Business Scenario 3", () => {
// check that FL is zero
expect(await poolController.firstLossBalance()).to.equal(0);
// Check that PM has no USDC balance
expect(await mockUSDC.balanceOf(poolManager.address)).to.equal(0);
expect(await mockUSDC.balanceOf(poolAdmin.address)).to.equal(0);

// +2 days, lenderA deposits
await advanceToDay(startTime, 2);
Expand Down Expand Up @@ -148,15 +148,15 @@ describe("Business Scenario 3", () => {

// +4 days, loan is funded
await advanceToDay(startTime, 4);
await fundLoan(loan, poolController, poolManager);
await fundLoan(loan, poolController, poolAdmin);
await loan.connect(borrower).drawdown(INPUTS.loan.principal);

// +7 days, lenderA requests 200k PT redemption
await advanceToDay(startTime, 7);
await pool.crank(); // crank runs, but is meaningless
await pool.connect(lenderA).requestRedeem(200_000_000_000);

// +8 days, lenderB requests 300k PT redeption
// +8 days, lenderB requests 300k PT redemption
await advanceToDay(startTime, 8);
await pool.connect(lenderB).requestRedeem(300_000_000_000);

Expand Down
2 changes: 1 addition & 1 deletion test/scenarios/business/4.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ describe("Business Scenario 4", () => {
await pool.crank(); // crank runs, but is meaningless
await pool.connect(lenderA).requestRedeem(200_000_000_000);

// +8 days, lenderB requests 300k PT redeption
// +8 days, lenderB requests 300k PT redemption
await advanceToDay(startTime, 8);
await pool.connect(lenderB).requestRedeem(300_000_000_000);

Expand Down
Loading

0 comments on commit 1cdc15c

Please sign in to comment.