Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KYC Sanction Expiration #320

Merged
merged 9 commits into from
Dec 6, 2024
39 changes: 34 additions & 5 deletions src/KintoID.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@

bytes32 public constant override KYC_PROVIDER_ROLE = keccak256("KYC_PROVIDER_ROLE");
bytes32 public constant override UPGRADER_ROLE = keccak256("UPGRADER_ROLE");
bytes32 public constant override GOVERNANCE_ROLE = keccak256("GOVERNANCE_ROLE");

address public immutable override walletFactory;
address public immutable override faucet;

/* ============ State Variables ============ */

Expand All @@ -63,8 +67,12 @@
// Indicates which accounts are allowed to transfer their Kinto ID to another account
mapping(address => address) public override recoveryTargets;

address public immutable override walletFactory;
address public immutable override faucet;
/// @notice DEPRECATED: Address of an wallet factory.
address private __walletFactory;

/// @notice Mapping of an user to a timestamp of when sanction was applied last time.
mapping(address => uint256) public sanctionedAt;

/* ============ Constructor & Initializers ============ */

/// @custom:oz-upgrades-unsafe-allow constructor
Expand Down Expand Up @@ -315,6 +323,9 @@
meta.updatedAt = block.timestamp;
lastMonitoredAt = block.timestamp;
emit SanctionAdded(_account, _countryId, block.timestamp);

// Set the timestamp when the sanction was added
sanctionedAt[_account] = block.timestamp;
}
}

Expand Down Expand Up @@ -361,7 +372,14 @@
* @return true if the account is sanctions safe.
*/
function isSanctionsSafe(address _account) public view virtual override returns (bool) {
return isSanctionsMonitored(7) && _kycmetas[_account].sanctionsCount == 0;
Metadata storage meta = _kycmetas[_account];
if (meta.sanctionsCount > 0 || !isSanctionsMonitored(7)) {
// If the sanction is not confirmed within 3 days, consider the account sanctions safe
return (block.timestamp - sanctionedAt[_account]) > 3 days;
}

// If the account has no sanctions, consider it sanctions safe
return true;
ylv-io marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand All @@ -371,7 +389,18 @@
* @return true if the account is sanctions safe in a given country.
*/
function isSanctionsSafeIn(address _account, uint16 _countryId) external view virtual override returns (bool) {
return isSanctionsMonitored(7) && !_kycmetas[_account].sanctions.get(_countryId);
if (_kycmetas[_account].sanctions.get(_countryId) || !isSanctionsMonitored(7)) {
// If the sanction is not confirmed within 3 days, consider the account sanctions safe
return (block.timestamp - sanctionedAt[_account]) > 3 days;
}

// If the account has no sanctions, consider it sanctions safe
return true;
ylv-io marked this conversation as resolved.
Show resolved Hide resolved
}

function confirmSanction(address _account) external onlyRole(GOVERNANCE_ROLE) {
// reset sanction timestamp to make the sanction indefinte
sanctionedAt[_account] = 0;

Check warning on line 403 in src/KintoID.sol

View check run for this annotation

Codecov / codecov/patch

src/KintoID.sol#L403

Added line #L403 was not covered by tests
}

/**
Expand All @@ -386,7 +415,7 @@
/**
* @dev Returns whether the KYC account is an individual
* @param _account account to be checked.
* @return true if the account is an indivdual.
* @return true if the account is an individual.
*/
function isIndividual(address _account) external view override returns (bool) {
return _kycmetas[_account].individual;
Expand Down
2 changes: 2 additions & 0 deletions src/interfaces/IKintoID.sol
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ interface IKintoID {

function UPGRADER_ROLE() external view returns (bytes32);

function GOVERNANCE_ROLE() external view returns (bytes32);

function lastMonitoredAt() external view returns (uint256);

function nonces(address _account) external view returns (uint256);
Expand Down
24 changes: 24 additions & 0 deletions test/unit/KintoID.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -381,9 +381,33 @@ contract KintoIDTest is SharedSetup {
traits[0] = 1;
_kintoID.mintIndividualKyc(sigdata, traits);
_kintoID.addSanction(_user, 1);

assertEq(_kintoID.isSanctionsSafeIn(_user, 1), false);
assertEq(_kintoID.isSanctionsSafe(_user), false);
assertEq(_kintoID.lastMonitoredAt(), block.timestamp);
}

function testAddSanction_WhenNotConfirmed() public {
vm.startPrank(_kycProvider);
IKintoID.SignatureData memory sigdata = _auxCreateSignature(_kintoID, _user, _userPk, block.timestamp + 1000);
uint16[] memory traits = new uint16[](1);
traits[0] = 1;
_kintoID.mintIndividualKyc(sigdata, traits);
_kintoID.addSanction(_user, 1);

assertEq(_kintoID.isSanctionsSafeIn(_user, 1), false);
assertEq(_kintoID.isSanctionsSafe(_user), false);
assertEq(_kintoID.lastMonitoredAt(), block.timestamp);
assertEq(_kintoID.sanctionedAt(_user), block.timestamp);

uint256 sanctionTime = block.timestamp;

vm.warp(block.timestamp + 3 days + 1);

assertEq(_kintoID.isSanctionsSafeIn(_user, 1), true);
assertEq(_kintoID.isSanctionsSafe(_user), true);
assertEq(_kintoID.lastMonitoredAt(), sanctionTime);
assertEq(_kintoID.sanctionedAt(_user), sanctionTime);
}

function testRemoveSancion() public {
Expand Down
Loading