Skip to content

Commit

Permalink
Add Celo related files (#453)
Browse files Browse the repository at this point in the history
## Summary
This PR adds the files that we used to introduce [native USDC to the
Celo
blockchain](https://www.circle.com/blog/usdc-now-available-on-celo).

- Add `ICeloGasToken` and `IFiatTokenFeeAdapter` per Celo documentation
- Add `FiatTokenFeeAdapterProxy` and `FiatTokenFeeAdapterV1` to support
USDC as
  gas on Celo
- Implement `debitGasFees` and `creditGasFees` in `FiatTokenCeloV2_2`
  • Loading branch information
circle-aloychan authored Apr 30, 2024
2 parents 2af605e + 7406115 commit 501555c
Show file tree
Hide file tree
Showing 27 changed files with 2,264 additions and 5 deletions.
19 changes: 19 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ MASTER_MINTER_OWNER_ADDRESS=
# The percentage to multiply gas usage estimations by (eg. 200 to double the estimation). Defaults to 130.
GAS_MULTIPLIER=110

################################
# Celo Specific Configurations #
################################

# [OPTIONAL] The address to a deployed FiatTokenCelo implementation contract.
# FIAT_TOKEN_CELO_IMPLEMENTATION_ADDRESS=

# [OPTIONAL] The address to a deployed FiatTokenProxy contract for FiatTokenCeloV2_2. Required for Celo Fee Adapter deployment.
# FIAT_TOKEN_CELO_PROXY_ADDRESS=

# [OPTIONAL] The address of the Fee Adapter Proxy's admin. Required for Celo Fee Adapter deployment.
# FEE_ADAPTER_PROXY_ADMIN_ADDRESS=

# [OPTIONAL] The number of decimals to scale the USDC contract to. Required for Celo Fee Adapter deployment.
# FEE_ADAPTER_DECIMALS=

################################
# Miscellaneous Configurations #
################################
Expand All @@ -79,3 +95,6 @@ BLACKLIST_FILE_NAME=blacklist.remote.json

# [OPTIONAL] The API key to an Etherscan flavor block explorer.
# ETHERSCAN_KEY=

# [OPTIONAL] The number of runs the Solidity optimizers should perform. Defaults to 10000000.
# OPTIMIZER_RUNS=
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ on:
branches: [master]
pull_request:

# Celo USDC contracts violate Spurious Dragon with existing configs, so
# we need to lower the number of runs to decrease the contract size.
env:
OPTIMIZER_RUNS: 81250

jobs:
run_ci_tests:
runs-on: ubuntu-latest
Expand Down
13 changes: 12 additions & 1 deletion @types/AnyFiatTokenV2Instance.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import { FiatTokenV2Instance } from "./generated/FiatTokenV2";
import { FiatTokenV2_1Instance } from "./generated/FiatTokenV2_1";
import { FiatTokenV2_2Instance } from "./generated/FiatTokenV2_2";
import { FiatTokenCeloV2_2Instance } from "./generated/FiatTokenCeloV2_2";

export interface FiatTokenV2_2InstanceExtended extends FiatTokenV2_2Instance {
permit?: typeof FiatTokenV2Instance.permit;
Expand All @@ -27,7 +28,17 @@ export interface FiatTokenV2_2InstanceExtended extends FiatTokenV2_2Instance {
cancelAuthorization?: typeof FiatTokenV2Instance.cancelAuthorization;
}

export interface FiatTokenCeloV2_2InstanceExtended
extends FiatTokenCeloV2_2Instance {
permit?: typeof FiatTokenV2Instance.permit;
transferWithAuthorization?: typeof FiatTokenV2Instance.transferWithAuthorization;
receiveWithAuthorization?: typeof FiatTokenV2Instance.receiveWithAuthorization;
cancelAuthorization?: typeof FiatTokenV2Instance.cancelAuthorization;
mint: typeof FiatTokenV2Instance.mint;
}

export type AnyFiatTokenV2Instance =
| FiatTokenV2Instance
| FiatTokenV2_1Instance
| FiatTokenV2_2InstanceExtended;
| FiatTokenV2_2InstanceExtended
| FiatTokenCeloV2_2InstanceExtended;
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## 2.2.0, Celo variant (2024-04-08)

- Add `ICeloGasToken` and `IFiatTokenFeeAdapter` per Celo documentation
- Add `FiatTokenFeeAdapterProxy` and `FiatTokenFeeAdapterV1` to support USDC as
gas on Celo
- Implement `debitGasFees` and `creditGasFees` in `FiatTokenCeloV2_2`

## 2.2.0 (2023-11-09)

- Add ERC-1271 signature validation support to EIP-2612 and EIP-3009 functions
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,4 @@ address.
- [Deployment process](./doc/deployment.md)
- [Preparing an upgrade](./doc/upgrade.md)
- [Upgrading from v2.1 to v2.2](./doc/v2.2_upgrade.md)
- [Celo FiatToken extension](./doc/celo.md)
68 changes: 68 additions & 0 deletions contracts/interface/celo/ICeloGasToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* Copyright 2024 Circle Internet Financial, LTD. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

pragma solidity 0.6.12;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
* @dev Interface of the Celo gas token standard for contracts
* as defined at https://docs.celo.org/learn/add-gas-currency.
*/
interface ICeloGasToken is IERC20 {
/**
* @notice Reserve balance for making payments for gas in this FiatToken currency.
* @param from The address from which to reserve balance.
* @param value The amount of balance to reserve.
* @dev This function is called by the Celo protocol when paying for transaction fees in this
* currency. After the transaction is executed, unused gas is refunded to the sender and credited
* to the various fee recipients via a call to `creditGasFees`. The events emitted by `creditGasFees`
* reflect the *net* gas fee payments for the transaction.
*/
function debitGasFees(address from, uint256 value) external;

/**
* @notice Credit balances of original payer and various fee recipients
* after having made payments for gas in the form of this FiatToken currency.
* @param from The original payer address from which balance was reserved via `debitGasFees`.
* @param feeRecipient The main fee recipient address.
* @param gatewayFeeRecipient Gateway address.
* @param communityFund Celo Community Fund address.
* @param refund Amount to be refunded by the VM to `from`.
* @param tipTxFee Amount to distribute to `feeRecipient`.
* @param gatewayFee Amount to distribute to `gatewayFeeRecipient`; this is deprecated and will always be 0.
* @param baseTxFee Amount to distribute to `communityFund`.
* @dev This function is called by the Celo protocol when paying for transaction fees in this
* currency. After the transaction is executed, unused gas is refunded to the sender and credited
* to the various fee recipients via a call to `creditGasFees`. The events emitted by `creditGasFees`
* reflect the *net* gas fee payments for the transaction. As an invariant, the original debited amount
* will always equal (refund + tipTxFee + gatewayFee + baseTxFee). Though the amount debited in debitGasFees
* is always equal to (refund + tipTxFee + gatewayFee + baseTxFee), in practice, the gateway fee is never
* used (0) and should ideally be ignored except in the function signature to optimize gas savings.
*/
function creditGasFees(
address from,
address feeRecipient,
address gatewayFeeRecipient,
address communityFund,
uint256 refund,
uint256 tipTxFee,
uint256 gatewayFee,
uint256 baseTxFee
) external;
}
29 changes: 29 additions & 0 deletions contracts/interface/celo/IDecimals.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Copyright 2024 Circle Internet Financial, LTD. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

pragma solidity 0.6.12;

/**
* @dev Interface for a contract, namely a currency token, that
* exposes how many decimals it has. While IERC20 does not define
* a `decimals` field, in practice, almost all standard ERC20s do
* themselves have a `decimals` field.
*/
interface IDecimals {
function decimals() external view returns (uint8);
}
79 changes: 79 additions & 0 deletions contracts/interface/celo/IFiatTokenFeeAdapter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Copyright 2024 Circle Internet Financial, LTD. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

pragma solidity 0.6.12;

/**
* @dev Barebones interface of the fee currency adapter standard for
* ERC-20 gas tokens that do not operate with 18 decimals. At a mini-
* mum, an implementation must support balance queries, debiting, and
* crediting to work with the Celo VM.
*/
interface IFiatTokenFeeAdapter {
/**
* @notice Return the balance of the address specified, but this balance
* is scaled appropriately to the number of decimals on this adapter.
* @dev The Celo VM calls balanceOf during its fee calculations on custom
* currencies to ensure that the holder has enough; since the VM debits
* and credits upscaled values, it needs to reference upscaled balances
* as well. See
* https://github.com/celo-org/celo-blockchain/blob/3808c45addf56cf547581599a1cb059bc4ae5089/core/state_transition.go#L321.
*/
function balanceOf(address account) external view returns (uint256);

/**
* @notice Reserve *adapted* balance for making payments for gas in this FiatToken currency.
* @param from The address from which to reserve balance.
* @param value The amount of balance to reserve.
* @dev This function is called by the Celo protocol when paying for transaction fees in this
* currency. After the transaction is executed, unused gas is refunded to the sender and credited
* to the various fee recipients via a call to `creditGasFees`. The events emitted by `creditGasFees`
* reflect the *net* gas fee payments for the transaction.
*/
function debitGasFees(address from, uint256 value) external;

/**
* @notice Credit *adapted* balances of original payer and various fee recipients
* after having made payments for gas in the form of this FiatToken currency.
* @param from The original payer address from which balance was reserved via `debitGasFees`.
* @param feeRecipient The main fee recipient address.
* @param gatewayFeeRecipient Gateway address.
* @param communityFund Celo Community Fund address.
* @param refund Amount to be refunded by the VM to `from`.
* @param tipTxFee Amount to distribute to `feeRecipient`.
* @param gatewayFee Amount to distribute to `gatewayFeeRecipient`; this is deprecated and will always be 0.
* @param baseTxFee Amount to distribute to `communityFund`.
* @dev This function is called by the Celo protocol when paying for transaction fees in this
* currency. After the transaction is executed, unused gas is refunded to the sender and credited
* to the various fee recipients via a call to `creditGasFees`. The events emitted by `creditGasFees`
* reflect the *net* gas fee payments for the transaction. As an invariant, the original debited amount
* will always equal (refund + tipTxFee + gatewayFee + baseTxFee). Though the amount debited in debitGasFees
* is always equal to (refund + tipTxFee + gatewayFee + baseTxFee), in practice, the gateway fee is never
* used (0) and should ideally be ignored except in the function signature to optimize gas savings.
*/
function creditGasFees(
address from,
address feeRecipient,
address gatewayFeeRecipient,
address communityFund,
uint256 refund,
uint256 tipTxFee,
uint256 gatewayFee,
uint256 baseTxFee
) external;
}
47 changes: 47 additions & 0 deletions contracts/test/celo/MockFiatTokenCeloWithExposedFunctions.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Copyright 2024 Circle Internet Financial, LTD. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

pragma solidity 0.6.12;

import { FiatTokenCeloV2_2 } from "../../v2/celo/FiatTokenCeloV2_2.sol";

// solhint-disable func-name-mixedcase

/**
* @dev This contract is the same as FiatTokenCeloV2_2, except, for testing,
* it allows us to call internal sensitive functions for testing. These
* external test functions are prefixed with "internal_" to differentiate
* them from the main internal functions.
*/
contract MockFiatTokenCeloWithExposedFunctions is FiatTokenCeloV2_2 {
function internal_debitedValue() external view returns (uint256) {
return _debitedValue();
}

function internal_transferReservedGas(
address from,
address to,
uint256 value
) external onlyFeeCaller {
_transferReservedGas(from, to, value);
}

function internal_setBalance(address account, uint256 balance) external {
_setBalance(account, balance);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Copyright 2024 Circle Internet Financial, LTD. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

pragma solidity 0.6.12;

import { FiatTokenFeeAdapterV1 } from "../../v2/celo/FiatTokenFeeAdapterV1.sol";

// solhint-disable func-name-mixedcase

/**
* @dev This contract is the same as FiatTokenFeeAdapterV1, except, for testing,
* it allows us to call the internal upscaling and downscaling functions and
* allows us to override the call originator on debiting and crediting, as Web3JS
* and Ganache do not allow us to impersonate 0x0 (vm.prank) for tests.
*/
contract MockFiatTokenFeeAdapterWithExposedFunctions is FiatTokenFeeAdapterV1 {
address private _vmCallerAddress;

modifier onlyCeloVm() override {
require(
msg.sender == _vmCallerAddress,
"FiatTokenFeeAdapterV1: caller is not VM"
);
_;
}

function setVmCallerAddress(address newVmCallerAddress) external {
_vmCallerAddress = newVmCallerAddress;
}

function internal_debitedValue() external view returns (uint256) {
return _debitedValue;
}

function internal_upscale(uint256 value) external view returns (uint256) {
return _upscale(value);
}

function internal_downscale(uint256 value) external view returns (uint256) {
return _downscale(value);
}
}
Loading

0 comments on commit 501555c

Please sign in to comment.