mirror of
git://holbrook.no/erc20-demurrage-token
synced 2024-11-22 08:16:47 +01:00
Remove complex account period tracker in single mode
This commit is contained in:
parent
f338510a1d
commit
74ef57a6a7
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -6,7 +6,6 @@ export PYTHONPATH=.
|
|||||||
|
|
||||||
modes=(MultiNocap MultiCap SingleCap SingleNocap)
|
modes=(MultiNocap MultiCap SingleCap SingleNocap)
|
||||||
for m in ${modes[@]}; do
|
for m in ${modes[@]}; do
|
||||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_pure.py
|
|
||||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_period.py
|
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_period.py
|
||||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_basic.py
|
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_basic.py
|
||||||
done
|
done
|
||||||
@ -23,6 +22,7 @@ done
|
|||||||
|
|
||||||
modes=(MultiCap MultiNocap)
|
modes=(MultiCap MultiNocap)
|
||||||
for m in ${modes[@]}; do
|
for m in ${modes[@]}; do
|
||||||
|
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_pure.py
|
||||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_redistribution.py
|
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_redistribution.py
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@ -18,13 +18,8 @@ contract DemurrageTokenSingleCap {
|
|||||||
uint8 constant shiftRedistributionIsFractional = 255;
|
uint8 constant shiftRedistributionIsFractional = 255;
|
||||||
uint256 constant maskRedistributionIsFractional = 0x8000000000000000000000000000000000000000000000000000000000000000; // 1 << 255
|
uint256 constant maskRedistributionIsFractional = 0x8000000000000000000000000000000000000000000000000000000000000000; // 1 << 255
|
||||||
|
|
||||||
// Account bit field, with associated shifts and masks
|
// Account balances
|
||||||
// Mirrors structure of redistributions for consistency
|
mapping (address => uint256) account;
|
||||||
mapping (address => bytes32) account; // uint152(unused) | uint32(period) | uint72(value)
|
|
||||||
uint8 constant shiftAccountValue = 0;
|
|
||||||
uint256 constant maskAccountValue = 0x0000000000000000000000000000000000000000000000ffffffffffffffffff; // (1 << 72) - 1
|
|
||||||
uint8 constant shiftAccountPeriod = 72;
|
|
||||||
uint256 constant maskAccountPeriod = 0x00000000000000000000000000000000000000ffffffff000000000000000000; // ((1 << 32) - 1) << 72
|
|
||||||
|
|
||||||
// Cached demurrage amount, ppm with 38 digit resolution
|
// Cached demurrage amount, ppm with 38 digit resolution
|
||||||
uint128 public demurrageAmount;
|
uint128 public demurrageAmount;
|
||||||
@ -157,7 +152,7 @@ contract DemurrageTokenSingleCap {
|
|||||||
|
|
||||||
/// Balance unmodified by demurrage
|
/// Balance unmodified by demurrage
|
||||||
function baseBalanceOf(address _account) public view returns (uint256) {
|
function baseBalanceOf(address _account) public view returns (uint256) {
|
||||||
return uint256(account[_account]) & maskAccountValue;
|
return account[_account];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Increases base balance for a single account
|
/// Increases base balance for a single account
|
||||||
@ -173,11 +168,7 @@ contract DemurrageTokenSingleCap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
oldBalance = baseBalanceOf(_account);
|
oldBalance = baseBalanceOf(_account);
|
||||||
newBalance = oldBalance + _delta;
|
account[_account] = oldBalance + _delta;
|
||||||
require(uint160(newBalance) > uint160(oldBalance), 'ERR_WOULDWRAP'); // revert if increase would result in a wrapped value
|
|
||||||
workAccount &= (~maskAccountValue);
|
|
||||||
workAccount |= (newBalance & maskAccountValue);
|
|
||||||
account[_account] = bytes32(workAccount);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,10 +186,7 @@ contract DemurrageTokenSingleCap {
|
|||||||
|
|
||||||
oldBalance = baseBalanceOf(_account);
|
oldBalance = baseBalanceOf(_account);
|
||||||
require(oldBalance >= _delta, 'ERR_OVERSPEND'); // overspend guard
|
require(oldBalance >= _delta, 'ERR_OVERSPEND'); // overspend guard
|
||||||
newBalance = oldBalance - _delta;
|
account[_account] = oldBalance - _delta;
|
||||||
workAccount &= (~maskAccountValue);
|
|
||||||
workAccount |= (newBalance & maskAccountValue);
|
|
||||||
account[_account] = bytes32(workAccount);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,8 +195,8 @@ contract DemurrageTokenSingleCap {
|
|||||||
function mintTo(address _beneficiary, uint256 _amount) external returns (bool) {
|
function mintTo(address _beneficiary, uint256 _amount) external returns (bool) {
|
||||||
uint256 baseAmount;
|
uint256 baseAmount;
|
||||||
|
|
||||||
require(minter[msg.sender]);
|
require(minter[msg.sender], 'ERR_ACCESS');
|
||||||
require(_amount + totalSupply <= supplyCap);
|
require(_amount + totalSupply <= supplyCap, 'ERR_CAP');
|
||||||
|
|
||||||
changePeriod();
|
changePeriod();
|
||||||
baseAmount = _amount;
|
baseAmount = _amount;
|
||||||
@ -303,36 +291,6 @@ contract DemurrageTokenSingleCap {
|
|||||||
return lastRedistribution;
|
return lastRedistribution;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deserialize the pemurrage period for the given account is participating in
|
|
||||||
function accountPeriod(address _account) public view returns (uint256) {
|
|
||||||
return (uint256(account[_account]) & maskAccountPeriod) >> shiftAccountPeriod;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the given demurrage period as the currently participation period for the given address
|
|
||||||
function registerAccountPeriod(address _account, uint256 _period) private returns (bool) {
|
|
||||||
account[_account] &= bytes32(~maskAccountPeriod);
|
|
||||||
account[_account] |= bytes32((_period << shiftAccountPeriod) & maskAccountPeriod);
|
|
||||||
incrementRedistributionParticipants();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine whether the unit number is rounded down, rounded up or evenly divides.
|
|
||||||
// Returns 0 if evenly distributed, or the remainder as a positive number
|
|
||||||
// A _numParts value 0 will be interpreted as the value 1
|
|
||||||
function remainder(uint256 _numParts, uint256 _sumWhole) public pure returns (uint256) {
|
|
||||||
uint256 unit;
|
|
||||||
uint256 truncatedResult;
|
|
||||||
|
|
||||||
if (_numParts == 0) { // no division by zero please
|
|
||||||
revert('ERR_NUMPARTS_ZERO');
|
|
||||||
}
|
|
||||||
require(_numParts < _sumWhole); // At least you are never LESS than the sum of your parts. Think about that.
|
|
||||||
|
|
||||||
unit = _sumWhole / _numParts;
|
|
||||||
truncatedResult = unit * _numParts;
|
|
||||||
return _sumWhole - truncatedResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the amount sent to the sink address
|
// Returns the amount sent to the sink address
|
||||||
function applyDefaultRedistribution(bytes32 _redistribution) private returns (uint256) {
|
function applyDefaultRedistribution(bytes32 _redistribution) private returns (uint256) {
|
||||||
uint256 redistributionSupply;
|
uint256 redistributionSupply;
|
||||||
@ -355,24 +313,6 @@ contract DemurrageTokenSingleCap {
|
|||||||
return unit;
|
return unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sets the remainder bit for the given period and books the remainder to the sink address balance
|
|
||||||
// returns false if no change was made
|
|
||||||
function applyRemainderOnPeriod(uint256 _remainder, uint256 _period) private returns (bool) {
|
|
||||||
uint256 periodSupply;
|
|
||||||
|
|
||||||
if (_remainder == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: is this needed?
|
|
||||||
redistributions[_period-1] |= bytes32(maskRedistributionIsFractional);
|
|
||||||
|
|
||||||
periodSupply = toRedistributionSupply(redistributions[_period-1]);
|
|
||||||
increaseBaseBalance(sinkAddress, periodSupply - _remainder);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Calculate and cache the demurrage value corresponding to the (period of the) time of the method call
|
// Calculate and cache the demurrage value corresponding to the (period of the) time of the method call
|
||||||
function applyDemurrage() public returns (bool) {
|
function applyDemurrage() public returns (bool) {
|
||||||
uint128 epochPeriodCount;
|
uint128 epochPeriodCount;
|
||||||
@ -408,7 +348,6 @@ contract DemurrageTokenSingleCap {
|
|||||||
bytes32 nextRedistribution;
|
bytes32 nextRedistribution;
|
||||||
uint256 currentPeriod;
|
uint256 currentPeriod;
|
||||||
uint256 currentParticipants;
|
uint256 currentParticipants;
|
||||||
uint256 currentRemainder;
|
|
||||||
uint256 currentDemurrageAmount;
|
uint256 currentDemurrageAmount;
|
||||||
uint256 nextRedistributionDemurrage;
|
uint256 nextRedistributionDemurrage;
|
||||||
uint256 demurrageCounts;
|
uint256 demurrageCounts;
|
||||||
@ -437,7 +376,7 @@ contract DemurrageTokenSingleCap {
|
|||||||
nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply, nextPeriod);
|
nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply, nextPeriod);
|
||||||
redistributions.push(nextRedistribution);
|
redistributions.push(nextRedistribution);
|
||||||
|
|
||||||
currentRemainder = applyDefaultRedistribution(currentRedistribution);
|
applyDefaultRedistribution(currentRedistribution);
|
||||||
emit Period(nextPeriod);
|
emit Period(nextPeriod);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -471,22 +410,6 @@ contract DemurrageTokenSingleCap {
|
|||||||
return (valueFactor * _value) / 1000000;
|
return (valueFactor * _value) / 1000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the given account is participating in a period and that period has been crossed
|
|
||||||
// THEN increase the base value of the account with its share of the value reduction of the period
|
|
||||||
function applyRedistributionOnAccount(address _account) public returns (bool) {
|
|
||||||
uint256 period;
|
|
||||||
|
|
||||||
period = accountPeriod(_account);
|
|
||||||
if (period == 0 || period >= actualPeriod()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// zero out period for the account
|
|
||||||
account[_account] &= bytes32(~maskAccountPeriod);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inflates the given amount according to the current demurrage modifier
|
// Inflates the given amount according to the current demurrage modifier
|
||||||
function toBaseAmount(uint256 _value) public view returns (uint256) {
|
function toBaseAmount(uint256 _value) public view returns (uint256) {
|
||||||
return (_value * ppmDivider * 1000000) / demurrageAmount;
|
return (_value * ppmDivider * 1000000) / demurrageAmount;
|
||||||
@ -517,7 +440,6 @@ contract DemurrageTokenSingleCap {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Implements ERC20, triggers tax and/or redistribution
|
// Implements ERC20, triggers tax and/or redistribution
|
||||||
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
|
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
|
||||||
uint256 baseValue;
|
uint256 baseValue;
|
||||||
@ -541,9 +463,6 @@ contract DemurrageTokenSingleCap {
|
|||||||
increaseBaseBalance(_to, _value);
|
increaseBaseBalance(_to, _value);
|
||||||
|
|
||||||
period = actualPeriod();
|
period = actualPeriod();
|
||||||
if (_value >= minimumParticipantSpend && accountPeriod(_from) != period && _from != _to) {
|
|
||||||
registerAccountPeriod(_from, period);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,13 +18,8 @@ contract DemurrageTokenSingleNocap {
|
|||||||
uint8 constant shiftRedistributionIsFractional = 255;
|
uint8 constant shiftRedistributionIsFractional = 255;
|
||||||
uint256 constant maskRedistributionIsFractional = 0x8000000000000000000000000000000000000000000000000000000000000000; // 1 << 255
|
uint256 constant maskRedistributionIsFractional = 0x8000000000000000000000000000000000000000000000000000000000000000; // 1 << 255
|
||||||
|
|
||||||
// Account bit field, with associated shifts and masks
|
// Account balances
|
||||||
// Mirrors structure of redistributions for consistency
|
mapping (address => uint256) account;
|
||||||
mapping (address => bytes32) account; // uint152(unused) | uint32(period) | uint72(value)
|
|
||||||
uint8 constant shiftAccountValue = 0;
|
|
||||||
uint256 constant maskAccountValue = 0x0000000000000000000000000000000000000000000000ffffffffffffffffff; // (1 << 72) - 1
|
|
||||||
uint8 constant shiftAccountPeriod = 72;
|
|
||||||
uint256 constant maskAccountPeriod = 0x00000000000000000000000000000000000000ffffffff000000000000000000; // ((1 << 32) - 1) << 72
|
|
||||||
|
|
||||||
// Cached demurrage amount, ppm with 38 digit resolution
|
// Cached demurrage amount, ppm with 38 digit resolution
|
||||||
uint128 public demurrageAmount;
|
uint128 public demurrageAmount;
|
||||||
@ -153,7 +148,7 @@ contract DemurrageTokenSingleNocap {
|
|||||||
|
|
||||||
/// Balance unmodified by demurrage
|
/// Balance unmodified by demurrage
|
||||||
function baseBalanceOf(address _account) public view returns (uint256) {
|
function baseBalanceOf(address _account) public view returns (uint256) {
|
||||||
return uint256(account[_account]) & maskAccountValue;
|
return account[_account];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Increases base balance for a single account
|
/// Increases base balance for a single account
|
||||||
@ -169,11 +164,7 @@ contract DemurrageTokenSingleNocap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
oldBalance = baseBalanceOf(_account);
|
oldBalance = baseBalanceOf(_account);
|
||||||
newBalance = oldBalance + _delta;
|
account[_account] = oldBalance + _delta;
|
||||||
require(uint160(newBalance) > uint160(oldBalance), 'ERR_WOULDWRAP'); // revert if increase would result in a wrapped value
|
|
||||||
workAccount &= (~maskAccountValue);
|
|
||||||
workAccount |= (newBalance & maskAccountValue);
|
|
||||||
account[_account] = bytes32(workAccount);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,10 +182,7 @@ contract DemurrageTokenSingleNocap {
|
|||||||
|
|
||||||
oldBalance = baseBalanceOf(_account);
|
oldBalance = baseBalanceOf(_account);
|
||||||
require(oldBalance >= _delta, 'ERR_OVERSPEND'); // overspend guard
|
require(oldBalance >= _delta, 'ERR_OVERSPEND'); // overspend guard
|
||||||
newBalance = oldBalance - _delta;
|
account[_account] = oldBalance - _delta;
|
||||||
workAccount &= (~maskAccountValue);
|
|
||||||
workAccount |= (newBalance & maskAccountValue);
|
|
||||||
account[_account] = bytes32(workAccount);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,36 +286,6 @@ contract DemurrageTokenSingleNocap {
|
|||||||
return lastRedistribution;
|
return lastRedistribution;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deserialize the pemurrage period for the given account is participating in
|
|
||||||
function accountPeriod(address _account) public view returns (uint256) {
|
|
||||||
return (uint256(account[_account]) & maskAccountPeriod) >> shiftAccountPeriod;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the given demurrage period as the currently participation period for the given address
|
|
||||||
function registerAccountPeriod(address _account, uint256 _period) private returns (bool) {
|
|
||||||
account[_account] &= bytes32(~maskAccountPeriod);
|
|
||||||
account[_account] |= bytes32((_period << shiftAccountPeriod) & maskAccountPeriod);
|
|
||||||
incrementRedistributionParticipants();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine whether the unit number is rounded down, rounded up or evenly divides.
|
|
||||||
// Returns 0 if evenly distributed, or the remainder as a positive number
|
|
||||||
// A _numParts value 0 will be interpreted as the value 1
|
|
||||||
function remainder(uint256 _numParts, uint256 _sumWhole) public pure returns (uint256) {
|
|
||||||
uint256 unit;
|
|
||||||
uint256 truncatedResult;
|
|
||||||
|
|
||||||
if (_numParts == 0) { // no division by zero please
|
|
||||||
revert('ERR_NUMPARTS_ZERO');
|
|
||||||
}
|
|
||||||
require(_numParts < _sumWhole); // At least you are never LESS than the sum of your parts. Think about that.
|
|
||||||
|
|
||||||
unit = _sumWhole / _numParts;
|
|
||||||
truncatedResult = unit * _numParts;
|
|
||||||
return _sumWhole - truncatedResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the amount sent to the sink address
|
// Returns the amount sent to the sink address
|
||||||
function applyDefaultRedistribution(bytes32 _redistribution) private returns (uint256) {
|
function applyDefaultRedistribution(bytes32 _redistribution) private returns (uint256) {
|
||||||
uint256 redistributionSupply;
|
uint256 redistributionSupply;
|
||||||
@ -350,24 +308,6 @@ contract DemurrageTokenSingleNocap {
|
|||||||
return unit;
|
return unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sets the remainder bit for the given period and books the remainder to the sink address balance
|
|
||||||
// returns false if no change was made
|
|
||||||
function applyRemainderOnPeriod(uint256 _remainder, uint256 _period) private returns (bool) {
|
|
||||||
uint256 periodSupply;
|
|
||||||
|
|
||||||
if (_remainder == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: is this needed?
|
|
||||||
redistributions[_period-1] |= bytes32(maskRedistributionIsFractional);
|
|
||||||
|
|
||||||
periodSupply = toRedistributionSupply(redistributions[_period-1]);
|
|
||||||
increaseBaseBalance(sinkAddress, periodSupply - _remainder);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Calculate and cache the demurrage value corresponding to the (period of the) time of the method call
|
// Calculate and cache the demurrage value corresponding to the (period of the) time of the method call
|
||||||
function applyDemurrage() public returns (bool) {
|
function applyDemurrage() public returns (bool) {
|
||||||
uint128 epochPeriodCount;
|
uint128 epochPeriodCount;
|
||||||
@ -403,7 +343,6 @@ contract DemurrageTokenSingleNocap {
|
|||||||
bytes32 nextRedistribution;
|
bytes32 nextRedistribution;
|
||||||
uint256 currentPeriod;
|
uint256 currentPeriod;
|
||||||
uint256 currentParticipants;
|
uint256 currentParticipants;
|
||||||
uint256 currentRemainder;
|
|
||||||
uint256 currentDemurrageAmount;
|
uint256 currentDemurrageAmount;
|
||||||
uint256 nextRedistributionDemurrage;
|
uint256 nextRedistributionDemurrage;
|
||||||
uint256 demurrageCounts;
|
uint256 demurrageCounts;
|
||||||
@ -432,7 +371,7 @@ contract DemurrageTokenSingleNocap {
|
|||||||
nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply, nextPeriod);
|
nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply, nextPeriod);
|
||||||
redistributions.push(nextRedistribution);
|
redistributions.push(nextRedistribution);
|
||||||
|
|
||||||
currentRemainder = applyDefaultRedistribution(currentRedistribution);
|
applyDefaultRedistribution(currentRedistribution);
|
||||||
emit Period(nextPeriod);
|
emit Period(nextPeriod);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -466,24 +405,8 @@ contract DemurrageTokenSingleNocap {
|
|||||||
return (valueFactor * _value) / 1000000;
|
return (valueFactor * _value) / 1000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the given account is participating in a period and that period has been crossed
|
|
||||||
// THEN increase the base value of the account with its share of the value reduction of the period
|
|
||||||
function applyRedistributionOnAccount(address _account) public returns (bool) {
|
|
||||||
uint256 period;
|
|
||||||
|
|
||||||
period = accountPeriod(_account);
|
|
||||||
if (period == 0 || period >= actualPeriod()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// zero out period for the account
|
|
||||||
account[_account] &= bytes32(~maskAccountPeriod);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inflates the given amount according to the current demurrage modifier
|
// Inflates the given amount according to the current demurrage modifier
|
||||||
function toBaseAmount(uint256 _value) public view returns (uint256) {
|
function toBaseAmount(uint256 _value) public view returns (uint256) {
|
||||||
//return (_value * ppmDivider * 1000000) / toDemurrageAmount(demurrageModifier);
|
|
||||||
return (_value * ppmDivider * 1000000) / demurrageAmount;
|
return (_value * ppmDivider * 1000000) / demurrageAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,9 +459,6 @@ contract DemurrageTokenSingleNocap {
|
|||||||
increaseBaseBalance(_to, _value);
|
increaseBaseBalance(_to, _value);
|
||||||
|
|
||||||
period = actualPeriod();
|
period = actualPeriod();
|
||||||
if (_value >= minimumParticipantSpend && accountPeriod(_from) != period && _from != _to) {
|
|
||||||
registerAccountPeriod(_from, period);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user