-
Notifications
You must be signed in to change notification settings - Fork 376
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
FeeCurrency Adapter #10907
Merged
pahor167
merged 26 commits into
release/core-contracts/11_2
from
pahor/feeCurrencyWrapper
Jan 29, 2024
Merged
FeeCurrency Adapter #10907
Changes from 21 commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
ea1b600
FeeCurrency Adapter
pahor167 a2bf3a7
PR comments
pahor167 cf95eea
Format documents
pahor167 c6def5b
gitmodules
pahor167 85526d5
submodules added
pahor167 1b48947
solidity version fix
pahor167 8d8f2d6
OZ contracts same commit as master
pahor167 5d41cff
Revert "OZ contracts same commit as master"
pahor167 aea9743
OZ to same commit as master
pahor167 3f78efb
Getter&Setter for wrapped token
pahor167 c36e60e
Proxy added
pahor167 83d2eb4
split of FeeCurrencyAdapter
pahor167 8d943b4
prettier fix
pahor167 6d4f4f1
refactor
pahor167 0ed3120
Update packages/protocol/test-sol/stability/FeeCurrencyAdapter.t.sol
pahor167 0803164
PR comments
pahor167 eeb4643
Merge branch 'pahor/feeCurrencyWrapper' of https://github.com/celo-or…
pahor167 aa2c09e
Merge branch 'release/core-contracts/11_2' into pahor/feeCurrencyWrapper
pahor167 7acf5ef
PR comments
pahor167 a598b2b
Decimals + Total supply added
pahor167 05ba9f6
Merge branch 'release/core-contracts/11_2' into pahor/feeCurrencyWrapper
timmoreton e3707a4
Update packages/protocol/contracts-0.8/stability/FeeCurrencyAdapter.sol
martinvol f35c79c
Update packages/protocol/contracts-0.8/stability/FeeCurrencyAdapter.sol
martinvol b5b5ed6
Trigger
alvarof2 7671925
Wrapper reference removed
pahor167 71796da
interface added
pahor167 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
23 changes: 23 additions & 0 deletions
23
packages/protocol/contracts-0.8/stability/CeloFeeCurrencyAdapterOwnable.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// SPDX-License-Identifier: LGPL-3.0-only | ||
pragma solidity >=0.8.7 <0.8.20; | ||
|
||
import "./FeeCurrencyAdapterOwnable.sol"; | ||
|
||
contract CeloFeeCurrencyAdapterOwnable is FeeCurrencyAdapterOwnable { | ||
/** | ||
* @notice Sets initialized == true on implementation contracts | ||
* @param test Set to true to skip implementation initialization | ||
*/ | ||
constructor(bool test) FeeCurrencyAdapterOwnable(test) {} | ||
|
||
/** | ||
* @notice Returns the storage, major, minor, and patch version of the contract. | ||
* @return Storage version of the contract. | ||
* @return Major version of the contract. | ||
* @return Minor version of the contract. | ||
* @return Patch version of the contract. | ||
*/ | ||
function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) { | ||
return (1, 1, 0, 0); | ||
} | ||
} |
165 changes: 165 additions & 0 deletions
165
packages/protocol/contracts-0.8/stability/FeeCurrencyAdapter.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
// SPDX-License-Identifier: LGPL-3.0-only | ||
pragma solidity >=0.8.7 <0.8.20; | ||
|
||
import "@openzeppelin/contracts8/access/Ownable.sol"; | ||
import "@openzeppelin/contracts8/token/ERC20/IERC20.sol"; | ||
|
||
import "../../contracts/common/CalledByVm.sol"; | ||
import "../../contracts/common/Initializable.sol"; | ||
import "../../contracts/common/interfaces/ICeloVersionedContract.sol"; | ||
import "../../contracts/common/FixidityLib.sol"; | ||
import "../../contracts/stability/interfaces/ISortedOracles.sol"; | ||
import "./interfaces/IFeeCurrency.sol"; | ||
import "./interfaces/IDecimals.sol"; | ||
|
||
contract FeeCurrencyAdapter is Initializable, CalledByVm { | ||
martinvol marked this conversation as resolved.
Show resolved
Hide resolved
pahor167 marked this conversation as resolved.
Show resolved
Hide resolved
martinvol marked this conversation as resolved.
Show resolved
Hide resolved
|
||
IFeeCurrency public adaptedToken; | ||
|
||
uint96 public digitDifference; | ||
|
||
uint256 public debited; | ||
martinvol marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
string public name; | ||
string public symbol; | ||
|
||
uint8 public expectedDecimals; | ||
|
||
/** | ||
* @notice Sets initialized == true on implementation contracts | ||
* @param test Set to true to skip implementation initialization | ||
*/ | ||
constructor(bool test) public Initializable(test) {} | ||
|
||
/** | ||
* @notice Used in place of the constructor to allow the contract to be upgradable via proxy. | ||
* @param _adaptedToken The address of the adapted token. | ||
* @param _name The name of the adapted token. | ||
* @param _symbol The symbol of the adapted token. | ||
* @param _expectedDecimals The expected number of decimals of the adapted token. | ||
*/ | ||
function initialize( | ||
address _adaptedToken, | ||
string memory _name, | ||
string memory _symbol, | ||
uint8 _expectedDecimals | ||
) public virtual initializer { | ||
_setAdaptedToken(_adaptedToken); | ||
name = _name; | ||
symbol = _symbol; | ||
uint8 decimals = IDecimals(_adaptedToken).decimals(); | ||
require(decimals < _expectedDecimals, "Decimals of adapted token must be < expected decimals."); | ||
digitDifference = uint96(10**(_expectedDecimals - decimals)); | ||
mcortesi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
expectedDecimals = _expectedDecimals; | ||
} | ||
|
||
/** | ||
* @notice Gets the balance of the specified address with correct digits. | ||
* @param account The address to query the balance of. | ||
* @return The balance of the specified address. | ||
*/ | ||
function balanceOf(address account) public view returns (uint256) { | ||
return upscale(adaptedToken.balanceOf(account)); | ||
} | ||
|
||
/** | ||
* @notice Gets the total supply with correct digits. | ||
* @return The total supply. | ||
*/ | ||
function totalSupply() public view returns (uint256) { | ||
return upscale(adaptedToken.totalSupply()); | ||
} | ||
|
||
/** | ||
* @notice Gets the total supply with correct digits. | ||
* @return The total supply. | ||
*/ | ||
function decimals() public view returns (uint8) { | ||
return expectedDecimals; | ||
} | ||
|
||
/** | ||
* Downscales value to the adapted token's native digits and debits it. | ||
* @param from from address | ||
* @param value Debited value in the adapted digits. | ||
*/ | ||
function debitGasFees(address from, uint256 value) external onlyVm { | ||
pahor167 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
uint256 valueScaled = downscale(value); | ||
require(valueScaled > 0, "Must debit at least one token."); | ||
pahor167 marked this conversation as resolved.
Show resolved
Hide resolved
martinvol marked this conversation as resolved.
Show resolved
Hide resolved
|
||
debited = valueScaled; | ||
adaptedToken.debitGasFees(from, valueScaled); | ||
} | ||
|
||
/** | ||
* Downscales value to the adapted token's native digits and credits it. | ||
* @param refundRecipient The recipient of the refund. | ||
* @param tipRecipient The recipient of the tip. | ||
* @param _gatewayFeeRecipient The recipient of the gateway fee. Unused. | ||
* @param baseFeeRecipient The recipient of the base fee. | ||
* @param refundAmount The amount to refund (in adapted token digits). | ||
* @param tipAmount The amount to tip (in adapted token digits). | ||
* @param _gatewayFeeAmount The amount of the gateway fee (in adapted token digits). Unused. | ||
* @param baseFeeAmount The amount of the base fee (in adapted token digits). | ||
*/ | ||
function creditGasFees( | ||
pahor167 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
address refundRecipient, | ||
address tipRecipient, | ||
address _gatewayFeeRecipient, | ||
address baseFeeRecipient, | ||
uint256 refundAmount, | ||
uint256 tipAmount, | ||
uint256 _gatewayFeeAmount, | ||
uint256 baseFeeAmount | ||
) public onlyVm { | ||
if (debited == 0) { | ||
// When eth.estimateGas is called, this function is called but we don't want to credit anything. | ||
return; | ||
} | ||
|
||
uint256 refundScaled = downscale(refundAmount); | ||
uint256 tipTxFeeScaled = downscale(tipAmount); | ||
uint256 baseTxFeeScaled = downscale(baseFeeAmount); | ||
|
||
require( | ||
refundScaled + tipTxFeeScaled + baseTxFeeScaled <= debited, | ||
"Cannot credit more than debited." | ||
); | ||
|
||
uint256 roundingError = debited - (refundScaled + tipTxFeeScaled + baseTxFeeScaled); | ||
|
||
if (roundingError > 0) { | ||
baseTxFeeScaled += roundingError; | ||
} | ||
adaptedToken.creditGasFees( | ||
refundRecipient, | ||
tipRecipient, | ||
address(0), | ||
baseFeeRecipient, | ||
refundScaled, | ||
tipTxFeeScaled, | ||
0, | ||
baseTxFeeScaled | ||
); | ||
|
||
debited = 0; | ||
} | ||
|
||
/** | ||
* @notice Returns adapted token address. | ||
* @return The adapted token address. | ||
*/ | ||
function getAdaptedToken() external view returns (address) { | ||
return address(adaptedToken); | ||
} | ||
|
||
function upscale(uint256 value) internal view returns (uint256) { | ||
return value * digitDifference; | ||
} | ||
|
||
function downscale(uint256 value) internal view returns (uint256) { | ||
return value / digitDifference; | ||
} | ||
|
||
function _setAdaptedToken(address _adaptedToken) internal virtual { | ||
adaptedToken = IFeeCurrency(_adaptedToken); | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
packages/protocol/contracts-0.8/stability/FeeCurrencyAdapterOwnable.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// SPDX-License-Identifier: LGPL-3.0-only | ||
pragma solidity >=0.8.7 <0.8.20; | ||
|
||
import "@openzeppelin/contracts8/access/Ownable.sol"; | ||
import "@openzeppelin/contracts8/token/ERC20/IERC20.sol"; | ||
|
||
import "./FeeCurrencyAdapter.sol"; | ||
|
||
contract FeeCurrencyAdapterOwnable is FeeCurrencyAdapter, Ownable { | ||
/** | ||
* @notice Sets initialized == true on implementation contracts | ||
* @param test Set to true to skip implementation initialization | ||
*/ | ||
constructor(bool test) FeeCurrencyAdapter(test) {} | ||
|
||
/** | ||
* @notice Used in place of the constructor to allow the contract to be upgradable via proxy. | ||
* @param _adaptedToken The address of the adapted token. | ||
* @param _name The name of the adapted token. | ||
* @param _symbol The symbol of the adapted token. | ||
* @param _expectedDecimals The expected number of decimals of the adapted token. | ||
*/ | ||
function initialize( | ||
address _adaptedToken, | ||
string memory _name, | ||
string memory _symbol, | ||
uint8 _expectedDecimals | ||
) public override { | ||
_transferOwnership(msg.sender); | ||
super.initialize(_adaptedToken, _name, _symbol, _expectedDecimals); | ||
} | ||
|
||
/** | ||
* @notice Sets adapted token address. | ||
* @param _adaptedToken The address of the adapted token. | ||
*/ | ||
function setAdaptedToken(address _adaptedToken) public onlyOwner { | ||
_setAdaptedToken(_adaptedToken); | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
packages/protocol/contracts-0.8/stability/interfaces/IDecimals.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
pragma solidity ^0.8.13; | ||
|
||
interface IDecimals { | ||
function decimals() external view returns (uint8); | ||
} |
58 changes: 58 additions & 0 deletions
58
packages/protocol/contracts-0.8/stability/interfaces/IFeeCurrency.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
pragma solidity ^0.8.13; | ||
|
||
import "@openzeppelin/contracts8/token/ERC20/IERC20.sol"; | ||
|
||
interface IFeeCurrency is IERC20 { | ||
/* | ||
This interface should be implemented for tokens which are supposed to | ||
act as fee currencies on the Celo blockchain, meaning that they can be | ||
used to pay gas fees for CIP-64 transactions (and some older tx types). | ||
See https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip-0064.md | ||
|
||
Before executing a tx with non-empty feeCurrency field, the fee | ||
currency's `debitGasFees` function is called to reserve the maximum | ||
amount that tx can spend on gas. After the tx has been executed, the | ||
`creditGasFees` function is called to refund the unused gas and credit | ||
the spent fees to the correct recipients. Events which are raised inside | ||
these functions will show up for every transaction using the token as a | ||
fee currency. | ||
|
||
Requirements: | ||
- The functions will be called by the blockchain client with `msg.sender | ||
== address(0)`. If this condition is not met, the functions must | ||
revert to prevent malicious users from crediting their accounts directly. | ||
- `creditGasFees` must credit all specified amounts. If it impossible to | ||
credit one of the recipients for some reason, add the amount to the | ||
value credited to the first valid recipient. This is important to keep | ||
the debited and credited amounts consistent. | ||
*/ | ||
|
||
// Called before transaction execution to reserve the maximum amount of gas | ||
// that can be used by the transaction. | ||
// - The implementation must reduce `from`'s balance by `value`. | ||
// - Must revert if `msg.sender` is not the zero address. | ||
function debitGasFees(address from, uint256 value) external; | ||
|
||
/** | ||
* Called after transaction execution to refund the unused gas and credit the | ||
* spent fees to the correct recipients. | ||
* @param refundRecipient The recipient of the refund. | ||
* @param tipRecipient The recipient of the tip. | ||
* @param _gatewayFeeRecipient The recipient of the gateway fee. Unused. | ||
* @param baseFeeRecipient The recipient of the base fee. | ||
* @param refundAmount The amount to refund (in wrapped token digits). | ||
pahor167 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* @param tipAmount The amount to tip (in wrapped token digits). | ||
* @param _gatewayFeeAmount The amount of the gateway fee (in wrapped token digits). Unused. | ||
* @param baseFeeAmount The amount of the base fee (in wrapped token digits). | ||
*/ | ||
function creditGasFees( | ||
address refundRecipient, | ||
address tipRecipient, | ||
address _gatewayFeeRecipient, | ||
address baseFeeRecipient, | ||
uint256 refundAmount, | ||
uint256 tipAmount, | ||
uint256 _gatewayFeeAmount, | ||
uint256 baseFeeAmount | ||
) external; | ||
} |
6 changes: 6 additions & 0 deletions
6
packages/protocol/contracts/common/proxies/FeeCurrencyAdapterProxy.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
pragma solidity ^0.5.13; | ||
|
||
import "../Proxy.sol"; | ||
|
||
/* solhint-disable-next-line no-empty-blocks */ | ||
contract FeeCurrencyAdapterProxy is Proxy {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
stability may not be the same folder for this file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What other folder are you suggesting ?