Skip to content

Commit

Permalink
(circles): introduce event DiscountCost
Browse files Browse the repository at this point in the history
  • Loading branch information
benjaminbollen committed May 3, 2024
1 parent f17bb8f commit 40ad78b
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 22 deletions.
2 changes: 0 additions & 2 deletions src/circles/Circles.sol
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,6 @@ contract Circles is ERC1155 {
DiscountedBalances(_inflation_day_zero)
{}

// External functions

// Public functions

/**
Expand Down
46 changes: 42 additions & 4 deletions src/circles/DiscountedBalances.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ contract DiscountedBalances is Demurrage {
*/
mapping(uint256 => mapping(address => DiscountedBalance)) public discountedBalances;

// Events

event DiscountCost(address indexed account, uint256 indexed id, uint256 discountCost);

// Constructor

/**
Expand All @@ -30,10 +34,30 @@ contract DiscountedBalances is Demurrage {
* @param _account Address of the account to calculate the balance of
* @param _id Circles identifier for which to calculate the balance
* @param _day Day since inflation_day_zero to calculate the balance for
* @return balance_ The discounted balance of the account for the Circles identifier
* @return discountCost_ The discount cost of the demurrage of the balance since the last update
*/
function balanceOfOnDay(address _account, uint256 _id, uint64 _day) public view returns (uint256) {
function balanceOfOnDay(address _account, uint256 _id, uint64 _day)
public
view
returns (uint256 balance_, uint256 discountCost_)
{
DiscountedBalance memory discountedBalance = discountedBalances[_id][_account];
return _calculateDiscountedBalance(discountedBalance.balance, _day - discountedBalance.lastUpdatedDay);
if (_day < discountedBalance.lastUpdatedDay) {
// DiscountedBalances: day is before last updated day
revert CirclesERC1155DayBeforeLastUpdatedDay(_account, _id, _day, discountedBalance.lastUpdatedDay, 0);
}
uint256 dayDifference;
unchecked {
dayDifference = _day - discountedBalance.lastUpdatedDay;
}
// Calculate the discounted balance
balance_ = _calculateDiscountedBalance(discountedBalance.balance, dayDifference);
// Calculate the discount cost; this can be unchecked as cost is strict positive
unchecked {
discountCost_ = discountedBalance.balance - balance_;
}
return (balance_, discountCost_);
}

// Internal functions
Expand Down Expand Up @@ -74,8 +98,22 @@ contract DiscountedBalances is Demurrage {
*/
function _discountAndAddToBalance(address _account, uint256 _id, uint256 _value, uint64 _day) internal {
DiscountedBalance storage discountedBalance = discountedBalances[_id][_account];
uint256 discountedBalanceValue =
_calculateDiscountedBalance(discountedBalance.balance, _day - discountedBalance.lastUpdatedDay);
if (_day < discountedBalance.lastUpdatedDay) {
// DiscountedBalances: day is before last updated day
revert CirclesERC1155DayBeforeLastUpdatedDay(_account, _id, _day, discountedBalance.lastUpdatedDay, 1);
}
uint256 dayDifference;
unchecked {
dayDifference = _day - discountedBalance.lastUpdatedDay;
}
uint256 discountedBalanceValue = _calculateDiscountedBalance(discountedBalance.balance, dayDifference);
// Calculate the discount cost; this can be unchecked as cost is strict positive
unchecked {
uint256 discountCost_ = discountedBalance.balance - discountedBalanceValue;
if (discountCost_ > 0) {
emit DiscountCost(_account, _id, discountCost_);
}
}
uint256 newBalance = discountedBalanceValue + _value;
if (newBalance > MAX_VALUE) {
// DiscountedBalances: balance exceeds maximum value
Expand Down
14 changes: 7 additions & 7 deletions src/circles/ERC1155.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ abstract contract ERC1155 is DiscountedBalances, Context, ERC165, IERC1155, IERC
// Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
string private _uri;

// Events

event ConvertInflation(uint256 inflationValue, uint256 demurrageValue, uint64 day);

// Constructor

/**
Expand Down Expand Up @@ -73,7 +69,8 @@ abstract contract ERC1155 is DiscountedBalances, Context, ERC165, IERC1155, IERC
* @dev See {IERC1155-balanceOf}.
*/
function balanceOf(address _account, uint256 _id) public view returns (uint256) {
return balanceOfOnDay(_account, _id, day(block.timestamp));
(uint256 balance,) = balanceOfOnDay(_account, _id, day(block.timestamp));
return balance;
}

/**
Expand All @@ -93,7 +90,7 @@ abstract contract ERC1155 is DiscountedBalances, Context, ERC165, IERC1155, IERC
uint256[] memory batchBalances = new uint256[](_accounts.length);

for (uint256 i = 0; i < _accounts.length; ++i) {
batchBalances[i] = balanceOfOnDay(_accounts.unsafeMemoryAccess(i), _ids.unsafeMemoryAccess(i), today);
(batchBalances[i],) = balanceOfOnDay(_accounts.unsafeMemoryAccess(i), _ids.unsafeMemoryAccess(i), today);
}

return batchBalances;
Expand Down Expand Up @@ -171,10 +168,13 @@ abstract contract ERC1155 is DiscountedBalances, Context, ERC165, IERC1155, IERC
uint256 value = values.unsafeMemoryAccess(i);

if (from != address(0)) {
uint256 fromBalance = balanceOfOnDay(from, id, today);
(uint256 fromBalance, uint256 discountCost) = balanceOfOnDay(from, id, today);
if (fromBalance < value) {
revert ERC1155InsufficientBalance(from, fromBalance, value, id);
}
if (discountCost > 0) {
emit DiscountCost(from, id, discountCost);
}
unchecked {
// Overflow not possible: value <= fromBalance
_updateBalance(from, id, fromBalance - value, today);
Expand Down
4 changes: 4 additions & 0 deletions src/errors/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ interface ICirclesERC1155Errors {
error CirclesERC1155MintBlocked(address human, address mintV1Status);

error CirclesERC1155AmountExceedsMaxUint190(address account, uint256 circlesId, uint256 amount, uint8 code);

error CirclesERC1155DayBeforeLastUpdatedDay(
address account, uint256 circlesId, uint64 day, uint64 lastUpdatedDay, uint8 code
);
}

interface ICirclesErrors {
Expand Down
12 changes: 3 additions & 9 deletions test/circles/Circles.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ contract CirclesTest is Test, TimeCirclesSetup, Approximation {
previousEndPeriod = endPeriod;

// Generate a pseudo-random number between 1 and 4
uint256 hoursSkip = uint256(keccak256(abi.encodePacked(block.timestamp, i, uint256(0)))) % 4 + 1;
uint256 hoursSkip = uint256(keccak256(abi.encodePacked(block.timestamp, i, uint256(0)))) % 34 + 1;
uint256 secondsSkip = uint256(keccak256(abi.encodePacked(block.timestamp, i, uint256(1)))) % 3600;

// Simulate passing of time variable windows of time (1-5 hours)
Expand Down Expand Up @@ -98,23 +98,17 @@ contract CirclesTest is Test, TimeCirclesSetup, Approximation {
assertEq(balance, expectedIssuance);
}

skipTime(26 hours);

// send 5 CRC from alice to bob
uint256 aliceBalance = circles.balanceOf(addresses[0], circlesIdentifiers[0]);
uint256 bobBalance = circles.balanceOf(addresses[1], circlesIdentifiers[0]);
// uint256 aliceInflationaryBalance = circles.inflationaryBalanceOf(addresses[0], circlesIdentifiers[0]);
// uint256 bobInflationaryBalance = circles.inflationaryBalanceOf(addresses[1], circlesIdentifiers[0]);
vm.prank(addresses[0]);
circles.safeTransferFrom(addresses[0], addresses[1], circlesIdentifiers[0], 5 * CRC, "");
uint256 aliceBalanceAfter = circles.balanceOf(addresses[0], circlesIdentifiers[0]);
uint256 bobBalanceAfter = circles.balanceOf(addresses[1], circlesIdentifiers[0]);
// uint256 aliceInflationaryBalanceAfter = circles.inflationaryBalanceOf(addresses[0], circlesIdentifiers[0]);
// uint256 bobInflationaryBalanceAfter = circles.inflationaryBalanceOf(addresses[1], circlesIdentifiers[0]);
assertEq(aliceBalance - 5 * CRC, aliceBalanceAfter);
assertEq(bobBalance + 5 * CRC, bobBalanceAfter);
// assertEq(
// aliceInflationaryBalance - aliceInflationaryBalanceAfter,
// bobInflationaryBalanceAfter - bobInflationaryBalance
// );
}

// Private functions
Expand Down
3 changes: 3 additions & 0 deletions test/hub/PathTransferHub.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ contract HubPathTransferTest is Test, TimeCirclesSetup, HumanRegistration, Appro
// first four avatars have a linear bi-directional trust
uint256 M = N;

// induce demurrage for the path transfer of the balances
skipTime(2 days);

// Flow matrix for transferring Circles from Alice to David
// with indication of which Circles are being sent
// A B C D
Expand Down

0 comments on commit 40ad78b

Please sign in to comment.