From 1aa50421fd6b4cad5fe47b635e6b739805cfa045 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Fri, 7 Jun 2024 18:56:22 +0100 Subject: [PATCH 1/2] (circle): re-introduce inflationary balance of and safeTransfer (but no batch equivalents) --- src/circles/Circles.sol | 35 ++++++++++++++++++++++++++++++ src/circles/Demurrage.sol | 5 +++-- src/circles/DiscountedBalances.sol | 10 +++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/circles/Circles.sol b/src/circles/Circles.sol index aeb2394..24a10b8 100644 --- a/src/circles/Circles.sol +++ b/src/circles/Circles.sol @@ -128,6 +128,41 @@ contract Circles is ERC1155, ICirclesErrors { ); } + /** + * Inflationary balance of an account for a Circles identifier. Careful, + * calculating the inflationary balance can introduce numerical errors + * in the least significant digits (order of few attoCircles). + * @param _account Address for which the balance is queried. + * @param _id Circles identifier for which the balance is queried. + */ + function inflationaryBalanceOf(address _account, uint256 _id) public view returns (uint256) { + return _inflationaryBalanceOf(_account, _id); + } + + /** + * @notice safeInflationaryTransferFrom transfers Circles from one address to another by specifying inflationary units. + * @param _from Address from which the Circles are transferred. + * @param _to Address to which the Circles are transferred. + * @param _id Circles indentifier for which the Circles are transferred. + * @param _inflationaryValue Inflationary value of the Circles transferred. + * @param _data Data to pass to the receiver. + */ + function safeInflationaryTransferFrom( + address _from, + address _to, + uint256 _id, + uint256 _inflationaryValue, + bytes memory _data + ) public { + address sender = _msgSender(); + if (_from != sender && !isApprovedForAll(_from, sender)) { + revert ERC1155MissingApprovalForAll(sender, _from); + } + // convert inflationary value to todays demurrage value + uint256 value = convertInflationaryToDemurrageValue(_inflationaryValue, day(block.timestamp)); + _safeTransferFrom(_from, _to, _id, value, _data); + } + // Internal functions /** diff --git a/src/circles/Demurrage.sol b/src/circles/Demurrage.sol index 18b461e..98fd48a 100644 --- a/src/circles/Demurrage.sol +++ b/src/circles/Demurrage.sol @@ -189,7 +189,8 @@ contract Demurrage is ICirclesDemurrageErrors { // calculate the demurrage value by multiplying the value by GAMMA^days // note: GAMMA < 1, so multiplying by a power of it, returns a smaller number, // so we lose the least significant bits, but our ground truth is the demurrage value, - // and the inflationary value the numerical approximation. + // and the inflationary value is a numerical approximation (where the least significant digits + // are not reliable). int128 r = Math64x64.pow(GAMMA_64x64, uint256(_day)); return Math64x64.mulu(r, _inflationaryValue); } @@ -270,7 +271,7 @@ contract Demurrage is ICirclesDemurrageErrors { function _calculateInflationaryBalance(uint256 _balance, uint256 _dayUpdated) internal pure returns (uint256) { // calculate the inflationary balance by dividing the balance by GAMMA^days // note: GAMMA < 1, so dividing by a power of it, returns a bigger number, - // so the numerical inprecision is in the least significant bits. + // so the numerical imprecision is in the least significant bits. int128 i = Math64x64.pow(BETA_64x64, _dayUpdated); return Math64x64.mulu(i, _balance); } diff --git a/src/circles/DiscountedBalances.sol b/src/circles/DiscountedBalances.sol index 38ea005..47a6636 100644 --- a/src/circles/DiscountedBalances.sol +++ b/src/circles/DiscountedBalances.sol @@ -73,6 +73,16 @@ contract DiscountedBalances is Demurrage { // Internal functions + /** + * @dev Calculate the inflationary balance of a discounted balance + * @param _account Address of the account to calculate the balance of + * @param _id Circles identifier for which to calculate the balance + */ + function _inflationaryBalanceOf(address _account, uint256 _id) internal view returns (uint256) { + DiscountedBalance memory discountedBalance = discountedBalances[_id][_account]; + return _calculateInflationaryBalance(discountedBalance.balance, discountedBalance.lastUpdatedDay); + } + /** * @dev Update the balance of an account for a given Circles identifier * @param _account Address of the account to update the balance of From 3560a969f727f9df97dfa39759883a75b1ab7ba8 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Fri, 7 Jun 2024 19:04:43 +0100 Subject: [PATCH 2/2] (circles): also add safeInflationaryBatchTransferFrom for batched inflationary transfers --- src/circles/Circles.sol | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/circles/Circles.sol b/src/circles/Circles.sol index 24a10b8..baf4829 100644 --- a/src/circles/Circles.sol +++ b/src/circles/Circles.sol @@ -163,6 +163,30 @@ contract Circles is ERC1155, ICirclesErrors { _safeTransferFrom(_from, _to, _id, value, _data); } + /** + * @notice safeInflationaryBatchTransferFrom transfers Circles from one address to another by specifying inflationary units. + * @param _from Address from which the Circles are transferred. + * @param _to Address to which the Circles are transferred. + * @param _ids Batch of Circles identifiers for which the Circles are transferred. + * @param _inflationaryValues Batch of inflationary values of the Circles transferred. + * @param _data Data to pass to the receiver. + */ + function safeInflationaryBatchTransferFrom( + address _from, + address _to, + uint256[] memory _ids, + uint256[] memory _inflationaryValues, + bytes memory _data + ) public { + address sender = _msgSender(); + if (_from != sender && !isApprovedForAll(_from, sender)) { + revert ERC1155MissingApprovalForAll(sender, _from); + } + uint64 today = day(block.timestamp); + uint256[] memory values = convertBatchInflationaryToDemurrageValues(_inflationaryValues, today); + _safeBatchTransferFrom(_from, _to, _ids, values, _data); + } + // Internal functions /**