diff --git a/contracts/contracts/libraries/usdc/FiatTokenProxy.sol.flatten b/contracts/contracts/libraries/usdc/FiatTokenProxy.sol.flatten index f21d33070..2f9e11121 100644 --- a/contracts/contracts/libraries/usdc/FiatTokenProxy.sol.flatten +++ b/contracts/contracts/libraries/usdc/FiatTokenProxy.sol.flatten @@ -1,29 +1,23 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: Apache-2.0 pragma solidity <0.8.0 =0.6.12 >=0.6.2; // contracts/upgradeability/Proxy.sol /** - + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. * - * Copyright (c) 2018 zOS Global Limited. + * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * 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 * - * The above copyright notice and this permission notice shall be included in - * copies or substantial portions of the Software. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ /** @@ -294,27 +288,21 @@ library Address { // contracts/upgradeability/UpgradeabilityProxy.sol /** - + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. * - * Copyright (c) 2018 zOS Global Limited. + * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * 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 * - * The above copyright notice and this permission notice shall be included in - * copies or substantial portions of the Software. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ /** @@ -394,27 +382,21 @@ contract UpgradeabilityProxy is Proxy { // contracts/upgradeability/AdminUpgradeabilityProxy.sol /** - + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. * - * Copyright (c) 2018 zOS Global Limited. + * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * 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 * - * The above copyright notice and this permission notice shall be included in - * copies or substantial portions of the Software. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ /** @@ -565,27 +547,21 @@ contract AdminUpgradeabilityProxy is UpgradeabilityProxy { // contracts/v1/FiatTokenProxy.sol /** - + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. * - * Copyright (c) 2018-2020 CENTRE SECZ + * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * 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 * - * The above copyright notice and this permission notice shall be included in - * copies or substantial portions of the Software. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ /** diff --git a/contracts/contracts/libraries/usdc/FiatTokenV2_1.sol.flatten b/contracts/contracts/libraries/usdc/FiatTokenV2_2.sol.flatten similarity index 58% rename from contracts/contracts/libraries/usdc/FiatTokenV2_1.sol.flatten rename to contracts/contracts/libraries/usdc/FiatTokenV2_2.sol.flatten index c1e9df3e9..9f8d3d419 100644 --- a/contracts/contracts/libraries/usdc/FiatTokenV2_1.sol.flatten +++ b/contracts/contracts/libraries/usdc/FiatTokenV2_2.sol.flatten @@ -1,30 +1,59 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: Apache-2.0 pragma solidity <0.8.0 =0.6.12 >=0.6.0 >=0.6.2; -// contracts/util/ECRecover.sol +// contracts/interface/IERC1271.sol /** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * * - * Copyright (c) 2016-2019 zOS Global Limited - * Copyright (c) 2018-2020 CENTRE SECZ + * 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 * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * http://www.apache.org/licenses/LICENSE-2.0 * - * The above copyright notice and this permission notice shall be included in - * copies or substantial portions of the Software. + * 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. + */ + +/** + * @dev Interface of the ERC1271 standard signature validation method for + * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. + */ +interface IERC1271 { + /** + * @dev Should return whether the signature provided is valid for the provided data + * @param hash Hash of the data to be signed + * @param signature Signature byte array associated with the provided data hash + * @return magicValue bytes4 magic value 0x1626ba7e when function passes + */ + function isValidSignature(bytes32 hash, bytes memory signature) + external + view + returns (bytes4 magicValue); +} + +// contracts/util/ECRecover.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + + * + * 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. */ /** @@ -74,6 +103,157 @@ library ECRecover { return signer; } + + /** + * @notice Recover signer's address from a signed message + * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/0053ee040a7ff1dbc39691c9e67a69f564930a88/contracts/utils/cryptography/ECDSA.sol + * @param digest Keccak-256 hash digest of the signed message + * @param signature Signature byte array associated with hash + * @return Signer address + */ + function recover(bytes32 digest, bytes memory signature) + internal + pure + returns (address) + { + require(signature.length == 65, "ECRecover: invalid signature length"); + + bytes32 r; + bytes32 s; + uint8 v; + + // ecrecover takes the signature parameters, and the only way to get them + // currently is to use assembly. + /// @solidity memory-safe-assembly + assembly { + r := mload(add(signature, 0x20)) + s := mload(add(signature, 0x40)) + v := byte(0, mload(add(signature, 0x60))) + } + return recover(digest, v, r, s); + } +} + +// contracts/util/EIP712.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * 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. + */ + +/** + * @title EIP712 + * @notice A library that provides EIP712 helper functions + */ +library EIP712 { + /** + * @notice Make EIP712 domain separator + * @param name Contract name + * @param version Contract version + * @param chainId Blockchain ID + * @return Domain separator + */ + function makeDomainSeparator( + string memory name, + string memory version, + uint256 chainId + ) internal view returns (bytes32) { + return + keccak256( + abi.encode( + // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") + 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, + keccak256(bytes(name)), + keccak256(bytes(version)), + chainId, + address(this) + ) + ); + } + + /** + * @notice Make EIP712 domain separator + * @param name Contract name + * @param version Contract version + * @return Domain separator + */ + function makeDomainSeparator(string memory name, string memory version) + internal + view + returns (bytes32) + { + uint256 chainId; + assembly { + chainId := chainid() + } + return makeDomainSeparator(name, version, chainId); + } +} + +// contracts/util/MessageHashUtils.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * 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. + */ + +/** + * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing. + * + * The library provides methods for generating a hash of a message that conforms to the + * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712] + * specifications. + */ +library MessageHashUtils { + /** + * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`). + * Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/21bb89ef5bfc789b9333eb05e3ba2b7b284ac77c/contracts/utils/cryptography/MessageHashUtils.sol + * + * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with + * `\x19\x01` and hashing the result. It corresponds to the hash signed by the + * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712. + * + * @param domainSeparator Domain separator + * @param structHash Hashed EIP-712 data struct + * @return digest The keccak256 digest of an EIP-712 typed data + */ + function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) + internal + pure + returns (bytes32 digest) + { + assembly { + let ptr := mload(0x40) + mstore(ptr, "\x19\x01") + mstore(add(ptr, 0x02), domainSeparator) + mstore(add(ptr, 0x22), structHash) + digest := keccak256(ptr, 0x42) + } + } } // contracts/v1/Ownable.sol @@ -168,37 +348,48 @@ contract Ownable { // contracts/v2/EIP712Domain.sol /** - + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. * - * Copyright (c) 2018-2020 CENTRE SECZ + * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * 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 * - * The above copyright notice and this permission notice shall be included in - * copies or substantial portions of the Software. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ +// solhint-disable func-name-mixedcase + /** * @title EIP712 Domain */ contract EIP712Domain { + // was originally DOMAIN_SEPARATOR + // but that has been moved to a method so we can override it in V2_2+ + bytes32 internal _DEPRECATED_CACHED_DOMAIN_SEPARATOR; + + /** + * @notice Get the EIP712 Domain Separator. + * @return The bytes32 EIP712 domain separator. + */ + function DOMAIN_SEPARATOR() external view returns (bytes32) { + return _domainSeparator(); + } + /** - * @dev EIP712 Domain Separator + * @dev Internal method to get the EIP712 Domain Separator. + * @return The bytes32 EIP712 domain separator. */ - bytes32 public DOMAIN_SEPARATOR; + function _domainSeparator() internal virtual view returns (bytes32) { + return _DEPRECATED_CACHED_DOMAIN_SEPARATOR; + } } // node_modules/@openzeppelin/contracts/math/SafeMath.sol @@ -678,114 +869,23 @@ library Address { } } -// contracts/util/EIP712.sol -/** - - * - * Copyright (c) 2018-2020 CENTRE SECZ - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * @title EIP712 - * @notice A library that provides EIP712 helper functions - */ -library EIP712 { - /** - * @notice Make EIP712 domain separator - * @param name Contract name - * @param version Contract version - * @return Domain separator - */ - function makeDomainSeparator(string memory name, string memory version) - internal - view - returns (bytes32) - { - uint256 chainId; - assembly { - chainId := chainid() - } - return - keccak256( - abi.encode( - // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") - 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, - keccak256(bytes(name)), - keccak256(bytes(version)), - chainId, - address(this) - ) - ); - } - - /** - * @notice Recover signer's address from a EIP712 signature - * @param domainSeparator Domain separator - * @param v v of the signature - * @param r r of the signature - * @param s s of the signature - * @param typeHashAndData Type hash concatenated with data - * @return Signer's address - */ - function recover( - bytes32 domainSeparator, - uint8 v, - bytes32 r, - bytes32 s, - bytes memory typeHashAndData - ) internal pure returns (address) { - bytes32 digest = keccak256( - abi.encodePacked( - "\x19\x01", - domainSeparator, - keccak256(typeHashAndData) - ) - ); - return ECRecover.recover(digest, v, r, s); - } -} - // contracts/v1/AbstractFiatTokenV1.sol /** - + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. * - * Copyright (c) 2018-2020 CENTRE SECZ + * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * 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 * - * The above copyright notice and this permission notice shall be included in - * copies or substantial portions of the Software. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ abstract contract AbstractFiatTokenV1 is IERC20 { @@ -804,43 +904,37 @@ abstract contract AbstractFiatTokenV1 is IERC20 { // contracts/v1/Blacklistable.sol /** - + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. * - * Copyright (c) 2018-2020 CENTRE SECZ + * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * 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 * - * The above copyright notice and this permission notice shall be included in - * copies or substantial portions of the Software. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ /** * @title Blacklistable Token * @dev Allows accounts to be blacklisted by a "blacklister" role */ -contract Blacklistable is Ownable { +abstract contract Blacklistable is Ownable { address public blacklister; - mapping(address => bool) internal blacklisted; + mapping(address => bool) internal _deprecatedBlacklisted; event Blacklisted(address indexed _account); event UnBlacklisted(address indexed _account); event BlacklisterChanged(address indexed newBlacklister); /** - * @dev Throws if called by any account other than the blacklister + * @dev Throws if called by any account other than the blacklister. */ modifier onlyBlacklister() { require( @@ -851,43 +945,48 @@ contract Blacklistable is Ownable { } /** - * @dev Throws if argument account is blacklisted - * @param _account The address to check + * @dev Throws if argument account is blacklisted. + * @param _account The address to check. */ modifier notBlacklisted(address _account) { require( - !blacklisted[_account], + !_isBlacklisted(_account), "Blacklistable: account is blacklisted" ); _; } /** - * @dev Checks if account is blacklisted - * @param _account The address to check + * @notice Checks if account is blacklisted. + * @param _account The address to check. + * @return True if the account is blacklisted, false if the account is not blacklisted. */ function isBlacklisted(address _account) external view returns (bool) { - return blacklisted[_account]; + return _isBlacklisted(_account); } /** - * @dev Adds account to blacklist - * @param _account The address to blacklist + * @notice Adds account to blacklist. + * @param _account The address to blacklist. */ function blacklist(address _account) external onlyBlacklister { - blacklisted[_account] = true; + _blacklist(_account); emit Blacklisted(_account); } /** - * @dev Removes account from blacklist - * @param _account The address to remove from the blacklist + * @notice Removes account from blacklist. + * @param _account The address to remove from the blacklist. */ function unBlacklist(address _account) external onlyBlacklister { - blacklisted[_account] = false; + _unBlacklist(_account); emit UnBlacklisted(_account); } + /** + * @notice Updates the blacklister address. + * @param _newBlacklister The address of the new blacklister. + */ function updateBlacklister(address _newBlacklister) external onlyOwner { require( _newBlacklister != address(0), @@ -896,14 +995,37 @@ contract Blacklistable is Ownable { blacklister = _newBlacklister; emit BlacklisterChanged(blacklister); } -} -// contracts/v1/Pausable.sol -/** + /** + * @dev Checks if account is blacklisted. + * @param _account The address to check. + * @return true if the account is blacklisted, false otherwise. + */ + function _isBlacklisted(address _account) + internal + virtual + view + returns (bool); + + /** + * @dev Helper method that blacklists an account. + * @param _account The address to blacklist. + */ + function _blacklist(address _account) internal virtual; + + /** + * @dev Helper method that unblacklists an account. + * @param _account The address to unblacklist. + */ + function _unBlacklist(address _account) internal virtual; +} + +// contracts/v1/Pausable.sol +/** * * Copyright (c) 2016 Smart Contract Solutions, Inc. - * Copyright (c) 2018-2020 CENTRE SECZ0 + * Copyright (c) 2018-2020 CENTRE SECZ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -978,7 +1100,8 @@ contract Pausable is Ownable { } /** - * @dev update the pauser role + * @notice Updates the pauser address. + * @param _newPauser The address of the new pauser. */ function updatePauser(address _newPauser) external onlyOwner { require( @@ -990,29 +1113,107 @@ contract Pausable is Ownable { } } -// contracts/v2/AbstractFiatTokenV2.sol +// contracts/util/SignatureChecker.sol /** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * * - * Copyright (c) 2018-2020 CENTRE SECZ + * 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 * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * http://www.apache.org/licenses/LICENSE-2.0 * - * The above copyright notice and this permission notice shall be included in - * copies or substantial portions of the Software. + * 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. + */ + +/** + * @dev Signature verification helper that can be used instead of `ECRecover.recover` to seamlessly support both ECDSA + * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/21bb89ef5bfc789b9333eb05e3ba2b7b284ac77c/contracts/utils/cryptography/SignatureChecker.sol + */ +library SignatureChecker { + /** + * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the + * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECRecover.recover`. + * @param signer Address of the claimed signer + * @param digest Keccak-256 hash digest of the signed message + * @param signature Signature byte array associated with hash + */ + function isValidSignatureNow( + address signer, + bytes32 digest, + bytes memory signature + ) external view returns (bool) { + if (!isContract(signer)) { + return ECRecover.recover(digest, signature) == signer; + } + return isValidERC1271SignatureNow(signer, digest, signature); + } + + /** + * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated + * against the signer smart contract using ERC1271. + * @param signer Address of the claimed signer + * @param digest Keccak-256 hash digest of the signed message + * @param signature Signature byte array associated with hash + * + * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus + * change through time. It could return true at block N and false at block N+1 (or the opposite). + */ + function isValidERC1271SignatureNow( + address signer, + bytes32 digest, + bytes memory signature + ) internal view returns (bool) { + (bool success, bytes memory result) = signer.staticcall( + abi.encodeWithSelector( + IERC1271.isValidSignature.selector, + digest, + signature + ) + ); + return (success && + result.length >= 32 && + abi.decode(result, (bytes32)) == + bytes32(IERC1271.isValidSignature.selector)); + } + + /** + * @dev Checks if the input address is a smart contract. + */ + function isContract(address addr) internal view returns (bool) { + uint256 size; + assembly { + size := extcodesize(addr) + } + return size > 0; + } +} + +// contracts/v2/AbstractFiatTokenV2.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * 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. */ abstract contract AbstractFiatTokenV2 is AbstractFiatTokenV1 { @@ -1101,27 +1302,21 @@ library SafeERC20 { // contracts/v1.1/Rescuable.sol /** - + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. * - * Copyright (c) 2018-2020 CENTRE SECZ + * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * 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 * - * The above copyright notice and this permission notice shall be included in - * copies or substantial portions of the Software. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ contract Rescuable is Ownable { @@ -1162,8 +1357,8 @@ contract Rescuable is Ownable { } /** - * @notice Assign the rescuer role to a given address. - * @param newRescuer New rescuer's address + * @notice Updates the rescuer address. + * @param newRescuer The address of the new rescuer. */ function updateRescuer(address newRescuer) external onlyOwner { require( @@ -1177,27 +1372,21 @@ contract Rescuable is Ownable { // contracts/v1/FiatTokenV1.sol /** - + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. * - * Copyright (c) 2018-2020 CENTRE SECZ + * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * 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 * - * The above copyright notice and this permission notice shall be included in - * copies or substantial portions of the Software. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ /** @@ -1214,7 +1403,10 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { address public masterMinter; bool internal initialized; - mapping(address => uint256) internal balances; + /// @dev A mapping that stores the balance and blacklist states for a given address. + /// The first bit defines whether the address is blacklisted (1 if blacklisted, 0 otherwise). + /// The last 255 bits define the balance for the address. + mapping(address => uint256) internal balanceAndBlacklistStates; mapping(address => mapping(address => uint256)) internal allowed; uint256 internal totalSupply_ = 0; mapping(address => bool) internal minters; @@ -1226,6 +1418,17 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { event MinterRemoved(address indexed oldMinter); event MasterMinterChanged(address indexed newMasterMinter); + /** + * @notice Initializes the fiat token contract. + * @param tokenName The name of the fiat token. + * @param tokenSymbol The symbol of the fiat token. + * @param tokenCurrency The fiat currency that the token represents. + * @param tokenDecimals The number of decimals that the token uses. + * @param newMasterMinter The masterMinter address for the fiat token. + * @param newPauser The pauser address for the fiat token. + * @param newBlacklister The blacklister address for the fiat token. + * @param newOwner The owner of the fiat token. + */ function initialize( string memory tokenName, string memory tokenSymbol, @@ -1266,7 +1469,7 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { } /** - * @dev Throws if called by any account other than a minter + * @dev Throws if called by any account other than a minter. */ modifier onlyMinters() { require(minters[msg.sender], "FiatToken: caller is not a minter"); @@ -1274,11 +1477,11 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { } /** - * @dev Function to mint tokens + * @notice Mints fiat tokens to an address. * @param _to The address that will receive the minted tokens. * @param _amount The amount of tokens to mint. Must be less than or equal * to the minterAllowance of the caller. - * @return A boolean that indicates if the operation was successful. + * @return True if the operation was successful. */ function mint(address _to, uint256 _amount) external @@ -1298,7 +1501,7 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { ); totalSupply_ = totalSupply_.add(_amount); - balances[_to] = balances[_to].add(_amount); + _setBalance(_to, _balanceOf(_to).add(_amount)); minterAllowed[msg.sender] = mintingAllowedAmount.sub(_amount); emit Mint(msg.sender, _to, _amount); emit Transfer(address(0), _to, _amount); @@ -1317,27 +1520,29 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { } /** - * @dev Get minter allowance for an account - * @param minter The address of the minter + * @notice Gets the minter allowance for an account. + * @param minter The address to check. + * @return The remaining minter allowance for the account. */ function minterAllowance(address minter) external view returns (uint256) { return minterAllowed[minter]; } /** - * @dev Checks if account is a minter - * @param account The address to check + * @notice Checks if an account is a minter. + * @param account The address to check. + * @return True if the account is a minter, false if the account is not a minter. */ function isMinter(address account) external view returns (bool) { return minters[account]; } /** - * @notice Amount of remaining tokens spender is allowed to transfer on - * behalf of the token owner - * @param owner Token owner's address - * @param spender Spender's address - * @return Allowance amount + * @notice Gets the remaining amount of fiat tokens a spender is allowed to transfer on + * behalf of the token owner. + * @param owner The token owner's address. + * @param spender The spender's address. + * @return The remaining allowance. */ function allowance(address owner, address spender) external @@ -1349,15 +1554,17 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { } /** - * @dev Get totalSupply of token + * @notice Gets the totalSupply of the fiat token. + * @return The totalSupply of the fiat token. */ function totalSupply() external override view returns (uint256) { return totalSupply_; } /** - * @dev Get token balance of an account - * @param account address The account + * @notice Gets the fiat token balance of an account. + * @param account The address to check. + * @return balance The fiat token balance of the account. */ function balanceOf(address account) external @@ -1365,18 +1572,18 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { view returns (uint256) { - return balances[account]; + return _balanceOf(account); } /** - * @notice Set spender's allowance over the caller's tokens to be a given - * value. - * @param spender Spender's address - * @param value Allowance amount - * @return True if successful + * @notice Sets a fiat token allowance for a spender to spend on behalf of the caller. + * @param spender The spender's address. + * @param value The allowance amount. + * @return True if the operation was successful. */ function approve(address spender, uint256 value) external + virtual override whenNotPaused notBlacklisted(msg.sender) @@ -1388,10 +1595,10 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { } /** - * @dev Internal function to set allowance - * @param owner Token owner's address - * @param spender Spender's address - * @param value Allowance amount + * @dev Internal function to set allowance. + * @param owner Token owner's address. + * @param spender Spender's address. + * @param value Allowance amount. */ function _approve( address owner, @@ -1405,11 +1612,12 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { } /** - * @notice Transfer tokens by spending allowance - * @param from Payer's address - * @param to Payee's address - * @param value Transfer amount - * @return True if successful + * @notice Transfers tokens from an address to another by spending the caller's allowance. + * @dev The caller must have some fiat token allowance on the payer's tokens. + * @param from Payer's address. + * @param to Payee's address. + * @param value Transfer amount. + * @return True if the operation was successful. */ function transferFrom( address from, @@ -1434,10 +1642,10 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { } /** - * @notice Transfer tokens from the caller - * @param to Payee's address - * @param value Transfer amount - * @return True if successful + * @notice Transfers tokens from the caller. + * @param to Payee's address. + * @param value Transfer amount. + * @return True if the operation was successful. */ function transfer(address to, uint256 value) external @@ -1452,10 +1660,10 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { } /** - * @notice Internal function to process transfers - * @param from Payer's address - * @param to Payee's address - * @param value Transfer amount + * @dev Internal function to process transfers. + * @param from Payer's address. + * @param to Payee's address. + * @param value Transfer amount. */ function _transfer( address from, @@ -1465,19 +1673,19 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); require( - value <= balances[from], + value <= _balanceOf(from), "ERC20: transfer amount exceeds balance" ); - balances[from] = balances[from].sub(value); - balances[to] = balances[to].add(value); + _setBalance(from, _balanceOf(from).sub(value)); + _setBalance(to, _balanceOf(to).add(value)); emit Transfer(from, to, value); } /** - * @dev Function to add/update a new minter - * @param minter The address of the minter - * @param minterAllowedAmount The minting amount allowed for the minter + * @notice Adds or updates a new minter with a mint allowance. + * @param minter The address of the minter. + * @param minterAllowedAmount The minting amount allowed for the minter. * @return True if the operation was successful. */ function configureMinter(address minter, uint256 minterAllowedAmount) @@ -1493,8 +1701,8 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { } /** - * @dev Function to remove a minter - * @param minter The address of the minter to remove + * @notice Removes a minter. + * @param minter The address of the minter to remove. * @return True if the operation was successful. */ function removeMinter(address minter) @@ -1509,10 +1717,10 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { } /** - * @dev allows a minter to burn some of its own tokens - * Validates that caller is a minter and that sender is not blacklisted - * amount is less than or equal to the minter's account balance - * @param _amount uint256 the amount of tokens to be burned + * @notice Allows a minter to burn some of its own tokens. + * @dev The caller must be a minter, must not be blacklisted, and the amount to burn + * should be less than or equal to the account's balance. + * @param _amount the amount of tokens to be burned. */ function burn(uint256 _amount) external @@ -1520,16 +1728,20 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { onlyMinters notBlacklisted(msg.sender) { - uint256 balance = balances[msg.sender]; + uint256 balance = _balanceOf(msg.sender); require(_amount > 0, "FiatToken: burn amount not greater than 0"); require(balance >= _amount, "FiatToken: burn amount exceeds balance"); totalSupply_ = totalSupply_.sub(_amount); - balances[msg.sender] = balance.sub(_amount); + _setBalance(msg.sender, balance.sub(_amount)); emit Burn(msg.sender, _amount); emit Transfer(msg.sender, address(0), _amount); } + /** + * @notice Updates the master minter address. + * @param _newMasterMinter The address of the new master minter. + */ function updateMasterMinter(address _newMasterMinter) external onlyOwner { require( _newMasterMinter != address(0), @@ -1538,31 +1750,87 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { masterMinter = _newMasterMinter; emit MasterMinterChanged(masterMinter); } + + /** + * + */ + function _blacklist(address _account) internal override { + _setBlacklistState(_account, true); + } + + /** + * + */ + function _unBlacklist(address _account) internal override { + _setBlacklistState(_account, false); + } + + /** + * @dev Helper method that sets the blacklist state of an account. + * @param _account The address of the account. + * @param _shouldBlacklist True if the account should be blacklisted, false if the account should be unblacklisted. + */ + function _setBlacklistState(address _account, bool _shouldBlacklist) + internal + virtual + { + _deprecatedBlacklisted[_account] = _shouldBlacklist; + } + + /** + * @dev Helper method that sets the balance of an account. + * @param _account The address of the account. + * @param _balance The new fiat token balance of the account. + */ + function _setBalance(address _account, uint256 _balance) internal virtual { + balanceAndBlacklistStates[_account] = _balance; + } + + /** + * + */ + function _isBlacklisted(address _account) + internal + virtual + override + view + returns (bool) + { + return _deprecatedBlacklisted[_account]; + } + + /** + * @dev Helper method to obtain the balance of an account. + * @param _account The address of the account. + * @return The fiat token balance of the account. + */ + function _balanceOf(address _account) + internal + virtual + view + returns (uint256) + { + return balanceAndBlacklistStates[_account]; + } } // contracts/v2/EIP2612.sol /** - + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. * - * Copyright (c) 2018-2020 CENTRE SECZ + * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * 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 * - * The above copyright notice and this permission notice shall be included in - * copies or substantial portions of the Software. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ /** @@ -1590,7 +1858,7 @@ abstract contract EIP2612 is AbstractFiatTokenV2, EIP712Domain { * @param owner Token owner's address (Authorizer) * @param spender Spender's address * @param value Amount of allowance - * @param deadline The time at which this expires (unix time) + * @param deadline The time at which the signature expires (unix time), or max uint256 value to signal no expiration * @param v v of the signature * @param r r of the signature * @param s s of the signature @@ -1604,18 +1872,49 @@ abstract contract EIP2612 is AbstractFiatTokenV2, EIP712Domain { bytes32 r, bytes32 s ) internal { - require(deadline >= now, "FiatTokenV2: permit is expired"); + _permit(owner, spender, value, deadline, abi.encodePacked(r, s, v)); + } - bytes memory data = abi.encode( - PERMIT_TYPEHASH, - owner, - spender, - value, - _permitNonces[owner]++, - deadline + /** + * @notice Verify a signed approval permit and execute if valid + * @dev EOA wallet signatures should be packed in the order of r, s, v. + * @param owner Token owner's address (Authorizer) + * @param spender Spender's address + * @param value Amount of allowance + * @param deadline The time at which the signature expires (unix time), or max uint256 value to signal no expiration + * @param signature Signature byte array signed by an EOA wallet or a contract wallet + */ + function _permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + bytes memory signature + ) internal { + require( + deadline == type(uint256).max || deadline >= now, + "FiatTokenV2: permit is expired" + ); + + bytes32 typedDataHash = MessageHashUtils.toTypedDataHash( + _domainSeparator(), + keccak256( + abi.encode( + PERMIT_TYPEHASH, + owner, + spender, + value, + _permitNonces[owner]++, + deadline + ) + ) ); require( - EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == owner, + SignatureChecker.isValidSignatureNow( + owner, + typedDataHash, + signature + ), "EIP2612: invalid signature" ); @@ -1625,27 +1924,21 @@ abstract contract EIP2612 is AbstractFiatTokenV2, EIP712Domain { // contracts/v2/EIP3009.sol /** - + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. * - * Copyright (c) 2018-2020 CENTRE SECZ + * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * 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 * - * The above copyright notice and this permission notice shall be included in - * copies or substantial portions of the Software. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ /** @@ -1717,20 +2010,52 @@ abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain { bytes32 r, bytes32 s ) internal { - _requireValidAuthorization(from, nonce, validAfter, validBefore); - - bytes memory data = abi.encode( - TRANSFER_WITH_AUTHORIZATION_TYPEHASH, + _transferWithAuthorization( from, to, value, validAfter, validBefore, - nonce + nonce, + abi.encodePacked(r, s, v) ); - require( - EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == from, - "FiatTokenV2: invalid signature" + } + + /** + * @notice Execute a transfer with a signed authorization + * @dev EOA wallet signatures should be packed in the order of r, s, v. + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param signature Signature byte array produced by an EOA wallet or a contract wallet + */ + function _transferWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + bytes memory signature + ) internal { + _requireValidAuthorization(from, nonce, validAfter, validBefore); + _requireValidSignature( + from, + keccak256( + abi.encode( + TRANSFER_WITH_AUTHORIZATION_TYPEHASH, + from, + to, + value, + validAfter, + validBefore, + nonce + ) + ), + signature ); _markAuthorizationAsUsed(from, nonce); @@ -1762,33 +2087,67 @@ abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain { bytes32 r, bytes32 s ) internal { - require(to == msg.sender, "FiatTokenV2: caller must be the payee"); - _requireValidAuthorization(from, nonce, validAfter, validBefore); - - bytes memory data = abi.encode( - RECEIVE_WITH_AUTHORIZATION_TYPEHASH, + _receiveWithAuthorization( from, to, value, validAfter, validBefore, - nonce - ); - require( - EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == from, - "FiatTokenV2: invalid signature" + nonce, + abi.encodePacked(r, s, v) ); - - _markAuthorizationAsUsed(from, nonce); - _transfer(from, to, value); } /** - * @notice Attempt to cancel an authorization - * @param authorizer Authorizer's address - * @param nonce Nonce of the authorization - * @param v v of the signature - * @param r r of the signature + * @notice Receive a transfer with a signed authorization from the payer + * @dev This has an additional check to ensure that the payee's address + * matches the caller of this function to prevent front-running attacks. + * EOA wallet signatures should be packed in the order of r, s, v. + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param signature Signature byte array produced by an EOA wallet or a contract wallet + */ + function _receiveWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + bytes memory signature + ) internal { + require(to == msg.sender, "FiatTokenV2: caller must be the payee"); + _requireValidAuthorization(from, nonce, validAfter, validBefore); + _requireValidSignature( + from, + keccak256( + abi.encode( + RECEIVE_WITH_AUTHORIZATION_TYPEHASH, + from, + to, + value, + validAfter, + validBefore, + nonce + ) + ), + signature + ); + + _markAuthorizationAsUsed(from, nonce); + _transfer(from, to, value); + } + + /** + * @notice Attempt to cancel an authorization + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @param v v of the signature + * @param r r of the signature * @param s s of the signature */ function _cancelAuthorization( @@ -1798,22 +2157,55 @@ abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain { bytes32 r, bytes32 s ) internal { - _requireUnusedAuthorization(authorizer, nonce); + _cancelAuthorization(authorizer, nonce, abi.encodePacked(r, s, v)); + } - bytes memory data = abi.encode( - CANCEL_AUTHORIZATION_TYPEHASH, + /** + * @notice Attempt to cancel an authorization + * @dev EOA wallet signatures should be packed in the order of r, s, v. + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @param signature Signature byte array produced by an EOA wallet or a contract wallet + */ + function _cancelAuthorization( + address authorizer, + bytes32 nonce, + bytes memory signature + ) internal { + _requireUnusedAuthorization(authorizer, nonce); + _requireValidSignature( authorizer, - nonce - ); - require( - EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == authorizer, - "FiatTokenV2: invalid signature" + keccak256( + abi.encode(CANCEL_AUTHORIZATION_TYPEHASH, authorizer, nonce) + ), + signature ); _authorizationStates[authorizer][nonce] = true; emit AuthorizationCanceled(authorizer, nonce); } + /** + * @notice Validates that signature against input data struct + * @param signer Signer's address + * @param dataHash Hash of encoded data struct + * @param signature Signature byte array produced by an EOA wallet or a contract wallet + */ + function _requireValidSignature( + address signer, + bytes32 dataHash, + bytes memory signature + ) private view { + require( + SignatureChecker.isValidSignatureNow( + signer, + MessageHashUtils.toTypedDataHash(_domainSeparator(), dataHash), + signature + ), + "FiatTokenV2: invalid signature" + ); + } + /** * @notice Check that an authorization is unused * @param authorizer Authorizer's address @@ -1865,27 +2257,21 @@ abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain { // contracts/v1.1/FiatTokenV1_1.sol /** - + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. * - * Copyright (c) 2018-2020 CENTRE SECZ + * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * 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 * - * The above copyright notice and this permission notice shall be included in - * copies or substantial portions of the Software. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ /** @@ -1898,27 +2284,21 @@ contract FiatTokenV1_1 is FiatTokenV1, Rescuable { // contracts/v2/FiatTokenV2.sol /** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * * - * Copyright (c) 2018-2020 CENTRE SECZ + * 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 * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * http://www.apache.org/licenses/LICENSE-2.0 * - * The above copyright notice and this permission notice shall be included in - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ /** @@ -1936,7 +2316,10 @@ contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 { // solhint-disable-next-line reason-string require(initialized && _initializedVersion == 0); name = newName; - DOMAIN_SEPARATOR = EIP712.makeDomainSeparator(newName, "2"); + _DEPRECATED_CACHED_DOMAIN_SEPARATOR = EIP712.makeDomainSeparator( + newName, + "2" + ); _initializedVersion = 1; } @@ -1948,6 +2331,7 @@ contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 { */ function increaseAllowance(address spender, uint256 increment) external + virtual whenNotPaused notBlacklisted(msg.sender) notBlacklisted(spender) @@ -1965,6 +2349,7 @@ contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 { */ function decreaseAllowance(address spender, uint256 decrement) external + virtual whenNotPaused notBlacklisted(msg.sender) notBlacklisted(spender) @@ -2072,7 +2457,7 @@ contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 { * @param owner Token owner's address (Authorizer) * @param spender Spender's address * @param value Amount of allowance - * @param deadline Expiration time, seconds since the epoch + * @param deadline The time at which the signature expires (unix time), or max uint256 value to signal no expiration * @param v v of the signature * @param r r of the signature * @param s s of the signature @@ -2085,12 +2470,18 @@ contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 { uint8 v, bytes32 r, bytes32 s - ) external whenNotPaused notBlacklisted(owner) notBlacklisted(spender) { + ) + external + virtual + whenNotPaused + notBlacklisted(owner) + notBlacklisted(spender) + { _permit(owner, spender, value, deadline, v, r, s); } /** - * @notice Internal function to increase the allowance by a given increment + * @dev Internal function to increase the allowance by a given increment * @param owner Token owner's address * @param spender Spender's address * @param increment Amount of increase @@ -2104,7 +2495,7 @@ contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 { } /** - * @notice Internal function to decrease the allowance by a given decrement + * @dev Internal function to decrease the allowance by a given decrement * @param owner Token owner's address * @param spender Spender's address * @param decrement Amount of decrease @@ -2127,27 +2518,21 @@ contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 { // contracts/v2/FiatTokenV2_1.sol /** - + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. * - * Copyright (c) 2018-2020 CENTRE SECZ + * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * 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 * - * The above copyright notice and this permission notice shall be included in - * copies or substantial portions of the Software. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ // solhint-disable func-name-mixedcase @@ -2165,11 +2550,11 @@ contract FiatTokenV2_1 is FiatTokenV2 { // solhint-disable-next-line reason-string require(_initializedVersion == 1); - uint256 lockedAmount = balances[address(this)]; + uint256 lockedAmount = _balanceOf(address(this)); if (lockedAmount > 0) { _transfer(address(this), lostAndFound, lockedAmount); } - blacklisted[address(this)] = true; + _blacklist(address(this)); _initializedVersion = 2; } @@ -2178,7 +2563,314 @@ contract FiatTokenV2_1 is FiatTokenV2 { * @notice Version string for the EIP712 domain separator * @return Version string */ - function version() external view returns (string memory) { + function version() external pure returns (string memory) { return "2"; } } + +// contracts/v2/FiatTokenV2_2.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * 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. + */ + + // solhint-disable-line no-unused-import + // solhint-disable-line no-unused-import + // solhint-disable-line no-unused-import + // solhint-disable-line no-unused-import + +// solhint-disable func-name-mixedcase + +/** + * @title FiatToken V2.2 + * @notice ERC20 Token backed by fiat reserves, version 2.2 + */ +contract FiatTokenV2_2 is FiatTokenV2_1 { + /** + * @notice Initialize v2.2 + * @param accountsToBlacklist A list of accounts to migrate from the old blacklist + * @param newSymbol New token symbol + * data structure to the new blacklist data structure. + */ + function initializeV2_2( + address[] calldata accountsToBlacklist, + string calldata newSymbol + ) external { + // solhint-disable-next-line reason-string + require(_initializedVersion == 2); + + // Update fiat token symbol + symbol = newSymbol; + + // Add previously blacklisted accounts to the new blacklist data structure + // and remove them from the old blacklist data structure. + for (uint256 i = 0; i < accountsToBlacklist.length; i++) { + require( + _deprecatedBlacklisted[accountsToBlacklist[i]], + "FiatTokenV2_2: Blacklisting previously unblacklisted account!" + ); + _blacklist(accountsToBlacklist[i]); + delete _deprecatedBlacklisted[accountsToBlacklist[i]]; + } + _blacklist(address(this)); + delete _deprecatedBlacklisted[address(this)]; + + _initializedVersion = 3; + } + + /** + * @dev Internal function to get the current chain id. + * @return The current chain id. + */ + function _chainId() internal virtual view returns (uint256) { + uint256 chainId; + assembly { + chainId := chainid() + } + return chainId; + } + + /** + * + */ + function _domainSeparator() internal override view returns (bytes32) { + return EIP712.makeDomainSeparator(name, "2", _chainId()); + } + + /** + * @notice Update allowance with a signed permit + * @dev EOA wallet signatures should be packed in the order of r, s, v. + * @param owner Token owner's address (Authorizer) + * @param spender Spender's address + * @param value Amount of allowance + * @param deadline The time at which the signature expires (unix time), or max uint256 value to signal no expiration + * @param signature Signature bytes signed by an EOA wallet or a contract wallet + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + bytes memory signature + ) external whenNotPaused { + _permit(owner, spender, value, deadline, signature); + } + + /** + * @notice Execute a transfer with a signed authorization + * @dev EOA wallet signatures should be packed in the order of r, s, v. + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param signature Signature bytes signed by an EOA wallet or a contract wallet + */ + function transferWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + bytes memory signature + ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) { + _transferWithAuthorization( + from, + to, + value, + validAfter, + validBefore, + nonce, + signature + ); + } + + /** + * @notice Receive a transfer with a signed authorization from the payer + * @dev This has an additional check to ensure that the payee's address + * matches the caller of this function to prevent front-running attacks. + * EOA wallet signatures should be packed in the order of r, s, v. + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param signature Signature bytes signed by an EOA wallet or a contract wallet + */ + function receiveWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + bytes memory signature + ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) { + _receiveWithAuthorization( + from, + to, + value, + validAfter, + validBefore, + nonce, + signature + ); + } + + /** + * @notice Attempt to cancel an authorization + * @dev Works only if the authorization is not yet used. + * EOA wallet signatures should be packed in the order of r, s, v. + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @param signature Signature bytes signed by an EOA wallet or a contract wallet + */ + function cancelAuthorization( + address authorizer, + bytes32 nonce, + bytes memory signature + ) external whenNotPaused { + _cancelAuthorization(authorizer, nonce, signature); + } + + /** + * @dev Helper method that sets the blacklist state of an account on balanceAndBlacklistStates. + * If _shouldBlacklist is true, we apply a (1 << 255) bitmask with an OR operation on the + * account's balanceAndBlacklistState. This flips the high bit for the account to 1, + * indicating that the account is blacklisted. + * + * If _shouldBlacklist if false, we reset the account's balanceAndBlacklistStates to their + * balances. This clears the high bit for the account, indicating that the account is unblacklisted. + * @param _account The address of the account. + * @param _shouldBlacklist True if the account should be blacklisted, false if the account should be unblacklisted. + */ + function _setBlacklistState(address _account, bool _shouldBlacklist) + internal + override + { + balanceAndBlacklistStates[_account] = _shouldBlacklist + ? balanceAndBlacklistStates[_account] | (1 << 255) + : _balanceOf(_account); + } + + /** + * @dev Helper method that sets the balance of an account on balanceAndBlacklistStates. + * Since balances are stored in the last 255 bits of the balanceAndBlacklistStates value, + * we need to ensure that the updated balance does not exceed (2^255 - 1). + * Since blacklisted accounts' balances cannot be updated, the method will also + * revert if the account is blacklisted + * @param _account The address of the account. + * @param _balance The new fiat token balance of the account (max: (2^255 - 1)). + */ + function _setBalance(address _account, uint256 _balance) internal override { + require( + _balance <= ((1 << 255) - 1), + "FiatTokenV2_2: Balance exceeds (2^255 - 1)" + ); + require( + !_isBlacklisted(_account), + "FiatTokenV2_2: Account is blacklisted" + ); + + balanceAndBlacklistStates[_account] = _balance; + } + + /** + * + */ + function _isBlacklisted(address _account) + internal + override + view + returns (bool) + { + return balanceAndBlacklistStates[_account] >> 255 == 1; + } + + /** + * @dev Helper method to obtain the balance of an account. Since balances + * are stored in the last 255 bits of the balanceAndBlacklistStates value, + * we apply a ((1 << 255) - 1) bit bitmask with an AND operation on the + * balanceAndBlacklistState to obtain the balance. + * @param _account The address of the account. + * @return The fiat token balance of the account. + */ + function _balanceOf(address _account) + internal + override + view + returns (uint256) + { + return balanceAndBlacklistStates[_account] & ((1 << 255) - 1); + } + + /** + * + */ + function approve(address spender, uint256 value) + external + override + whenNotPaused + returns (bool) + { + _approve(msg.sender, spender, value); + return true; + } + + /** + * + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external override whenNotPaused { + _permit(owner, spender, value, deadline, v, r, s); + } + + /** + * + */ + function increaseAllowance(address spender, uint256 increment) + external + override + whenNotPaused + returns (bool) + { + _increaseAllowance(msg.sender, spender, increment); + return true; + } + + /** + * + */ + function decreaseAllowance(address spender, uint256 decrement) + external + override + whenNotPaused + returns (bool) + { + _decreaseAllowance(msg.sender, spender, decrement); + return true; + } +} diff --git a/contracts/contracts/libraries/usdc/MigrationUSDC.sol b/contracts/contracts/libraries/usdc/MigrationUSDC.sol new file mode 100644 index 000000000..462aacb70 --- /dev/null +++ b/contracts/contracts/libraries/usdc/MigrationUSDC.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT + +pragma solidity =0.8.24; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; +import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; +import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; +import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; + +contract MigrationUSDC is OwnableUpgradeable, PausableUpgradeable, ReentrancyGuardUpgradeable { + using SafeERC20Upgradeable for IERC20Upgradeable; + + /// @dev Thrown the token balance is zero. + error ErrorTokenBalanceZero(); + + event Migrate(address indexed user, uint256 amount); + event Transfer(address indexed token, address indexed to, uint256 amount); + + /// @notice The address of old USDC address. + address public immutable OLD_USDC; + + /// @notice The address of new USDC address. + address public immutable NEW_USDC; + + /*************** + * Constructor * + ***************/ + /// @notice Constructor for `MigrationUSDC` implementation contract. + /// + /// @param _oldUSDC The address of old USDC in L2. + /// @param _newUSDC The address of new USDC in L2. + constructor(address _oldUSDC, address _newUSDC) { + _disableInitializers(); + + OLD_USDC = _oldUSDC; + NEW_USDC = _newUSDC; + } + + // initialize contract status + function initialize() external initializer { + __Ownable_init(); + __Pausable_init(); + __ReentrancyGuard_init(); + } + + // Transfer all old USDC to this contract and then transfer new USDC token to msg sender. + function migrate() external nonReentrant whenNotPaused { + // Get old USDC balance. + uint256 balance = IERC20Upgradeable(OLD_USDC).balanceOf(_msgSender()); + if (balance == 0) { + revert ErrorTokenBalanceZero(); + } + // Transfer token into this contract. + IERC20Upgradeable(OLD_USDC).safeTransferFrom(_msgSender(), address(this), balance); + // Transfer new USDC token to msg sender. + IERC20Upgradeable(NEW_USDC).transfer(_msgSender(), balance); + emit Migrate(_msgSender(), balance); + } + + // Transfer token to other address. + function transferToken(address _token, address _to, uint256 _amount) external onlyOwner { + uint256 balance = IERC20Upgradeable(_token).balanceOf(address(this)); + if (balance == 0) { + revert ErrorTokenBalanceZero(); + } + // transfer all token + if (balance < _amount) { + _amount = balance; + } + // Transfer token. + IERC20Upgradeable(_token).transfer(_to, _amount); + emit Transfer(_token, _to, _amount); + } + + // Change pause status + function setPause(bool status) external { + if (status) { + _requireNotPaused(); + _pause(); + } else { + _requirePaused(); + _unpause(); + } + } +} diff --git a/contracts/contracts/libraries/usdc/SignatureChecker.sol.flatten b/contracts/contracts/libraries/usdc/SignatureChecker.sol.flatten new file mode 100644 index 000000000..5f3ef0554 --- /dev/null +++ b/contracts/contracts/libraries/usdc/SignatureChecker.sol.flatten @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity =0.6.12; + +// contracts/interface/IERC1271.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * 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. + */ + +/** + * @dev Interface of the ERC1271 standard signature validation method for + * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. + */ +interface IERC1271 { + /** + * @dev Should return whether the signature provided is valid for the provided data + * @param hash Hash of the data to be signed + * @param signature Signature byte array associated with the provided data hash + * @return magicValue bytes4 magic value 0x1626ba7e when function passes + */ + function isValidSignature(bytes32 hash, bytes memory signature) + external + view + returns (bytes4 magicValue); +} + +// contracts/util/ECRecover.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * 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. + */ + +/** + * @title ECRecover + * @notice A library that provides a safe ECDSA recovery function + */ +library ECRecover { + /** + * @notice Recover signer's address from a signed message + * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/65e4ffde586ec89af3b7e9140bdc9235d1254853/contracts/cryptography/ECDSA.sol + * Modifications: Accept v, r, and s as separate arguments + * @param digest Keccak-256 hash digest of the signed message + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + * @return Signer address + */ + function recover( + bytes32 digest, + uint8 v, + bytes32 r, + bytes32 s + ) internal pure returns (address) { + // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature + // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines + // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most + // signatures from current libraries generate a unique signature with an s-value in the lower half order. + // + // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value + // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or + // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept + // these malleable signatures as well. + if ( + uint256(s) > + 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 + ) { + revert("ECRecover: invalid signature 's' value"); + } + + if (v != 27 && v != 28) { + revert("ECRecover: invalid signature 'v' value"); + } + + // If the signature is valid (and not malleable), return the signer address + address signer = ecrecover(digest, v, r, s); + require(signer != address(0), "ECRecover: invalid signature"); + + return signer; + } + + /** + * @notice Recover signer's address from a signed message + * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/0053ee040a7ff1dbc39691c9e67a69f564930a88/contracts/utils/cryptography/ECDSA.sol + * @param digest Keccak-256 hash digest of the signed message + * @param signature Signature byte array associated with hash + * @return Signer address + */ + function recover(bytes32 digest, bytes memory signature) + internal + pure + returns (address) + { + require(signature.length == 65, "ECRecover: invalid signature length"); + + bytes32 r; + bytes32 s; + uint8 v; + + // ecrecover takes the signature parameters, and the only way to get them + // currently is to use assembly. + /// @solidity memory-safe-assembly + assembly { + r := mload(add(signature, 0x20)) + s := mload(add(signature, 0x40)) + v := byte(0, mload(add(signature, 0x60))) + } + return recover(digest, v, r, s); + } +} + +// contracts/util/SignatureChecker.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * 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. + */ + +/** + * @dev Signature verification helper that can be used instead of `ECRecover.recover` to seamlessly support both ECDSA + * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets. + * + * Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/21bb89ef5bfc789b9333eb05e3ba2b7b284ac77c/contracts/utils/cryptography/SignatureChecker.sol + */ +library SignatureChecker { + /** + * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the + * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECRecover.recover`. + * @param signer Address of the claimed signer + * @param digest Keccak-256 hash digest of the signed message + * @param signature Signature byte array associated with hash + */ + function isValidSignatureNow( + address signer, + bytes32 digest, + bytes memory signature + ) external view returns (bool) { + if (!isContract(signer)) { + return ECRecover.recover(digest, signature) == signer; + } + return isValidERC1271SignatureNow(signer, digest, signature); + } + + /** + * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated + * against the signer smart contract using ERC1271. + * @param signer Address of the claimed signer + * @param digest Keccak-256 hash digest of the signed message + * @param signature Signature byte array associated with hash + * + * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus + * change through time. It could return true at block N and false at block N+1 (or the opposite). + */ + function isValidERC1271SignatureNow( + address signer, + bytes32 digest, + bytes memory signature + ) internal view returns (bool) { + (bool success, bytes memory result) = signer.staticcall( + abi.encodeWithSelector( + IERC1271.isValidSignature.selector, + digest, + signature + ) + ); + return (success && + result.length >= 32 && + abi.decode(result, (bytes32)) == + bytes32(IERC1271.isValidSignature.selector)); + } + + /** + * @dev Checks if the input address is a smart contract. + */ + function isContract(address addr) internal view returns (bool) { + uint256 size; + assembly { + size := extcodesize(addr) + } + return size > 0; + } +} diff --git a/contracts/tasks/token_deploy.ts b/contracts/tasks/token_deploy.ts index bd7a10ef3..81821d0dc 100644 --- a/contracts/tasks/token_deploy.ts +++ b/contracts/tasks/token_deploy.ts @@ -359,25 +359,26 @@ task("upgrade-l1-usdcgateway") const proxyAdmin = await ProxyAdminFactory.attach(taskArgs.proxyadmin) console.log(`proxy admin at ${proxyAdmin.address}`) - let res = await proxyAdmin.upgradeAndCall( + let res = await proxyAdmin.upgrade( taskArgs.l1usdcgatewayproxy, - gateway.address, - GatewayFactory.interface.encodeFunctionData("initialize", [ - taskArgs.counterpart, - taskArgs.router, - taskArgs.messenger - ] - ) + gateway.address ) let rec =await res.wait() console.log(`upgrade gateway ${rec.status == 1}`) + const gatewayProxy = GatewayFactory.attach(taskArgs.l1usdcgatewayproxy) + res = await gatewayProxy.initialize(taskArgs.counterpart,taskArgs.router,taskArgs.messenger) + rec =await res.wait() + console.log(`init gateway ${rec.status == 1}`) + + const owner = await gatewayProxy.owner() const counterpart = await gatewayProxy.counterpart() const router = await gatewayProxy.router() + const messenger = await gatewayProxy.messenger() const l1USDC = await gatewayProxy.l1USDC() const l2USDC = await gatewayProxy.l2USDC() - console.log(`gatewayProxy ${gatewayProxy.address}, counterpart ${counterpart}, router ${router}, l1USDC ${l1USDC}, l2USDC ${l2USDC}`) + console.log(`owner ${owner}, gatewayProxy ${gatewayProxy.address}, counterpart ${counterpart}, router ${router}, messenger ${messenger}, l1USDC ${l1USDC}, l2USDC ${l2USDC}`) }) task("upgrade-l2-usdcgateway") @@ -412,20 +413,17 @@ task("upgrade-l2-usdcgateway") const proxyAdmin = await ProxyAdminFactory.attach(taskArgs.proxyadmin) console.log(`proxy admin at ${proxyAdmin.address}`) - let res = await proxyAdmin.upgradeAndCall( + let res = await proxyAdmin.upgrade( taskArgs.l2usdcgatewayproxy, - gateway.address, - GatewayFactory.interface.encodeFunctionData("initialize", [ - taskArgs.counterpart, - taskArgs.router, - taskArgs.messenger - ] - ) + gateway.address ) let rec =await res.wait() console.log(`upgrade gateway ${rec.status == 1}`) const gatewayProxy = GatewayFactory.attach(taskArgs.l2usdcgatewayproxy) + res = await gatewayProxy.initialize( taskArgs.counterpart,taskArgs.router,taskArgs.messenger) + rec =await res.wait() + console.log(`init gateway ${rec.status == 1}`) const counterpart = await gatewayProxy.counterpart() const router = await gatewayProxy.router() const l1USDC = await gatewayProxy.l1USDC() @@ -501,3 +499,111 @@ task("upgrade-l2-usdc-v2-1") rec = await res.wait() console.log(`l2 usdc initializeV2_1 ${rec.status == 1}`) }) + +task("deploy-l1-proxy") + .addParam("proxyadmin") + .addParam("empty") + .setAction(async (taskArgs, hre) => { + // params check + if (!ethers.utils.isAddress(taskArgs.proxyadmin) || + !ethers.utils.isAddress(taskArgs.empty) + ) { + console.error(`address params check failed`) + return + } + + const TransparentProxyFactory = await hre.ethers.getContractFactory("TransparentUpgradeableProxy") + const proxy = await TransparentProxyFactory.deploy( + taskArgs.empty, //logic + taskArgs.proxyadmin, //admin + "0x" + ) + await proxy.deployed() + console.log(`proxy deployed at ${proxy.address}`) + }) + + +task("deploy-l2-usdcgateway") + .addParam("proxyadmin") + .addParam("l1token") + .addParam("l2token") + .addParam("counterpart") + .addParam("router") + .addParam("messenger") + .setAction(async (taskArgs, hre) => { + // params check + if (!ethers.utils.isAddress(taskArgs.proxyadmin) || + !ethers.utils.isAddress(taskArgs.l1token) || + !ethers.utils.isAddress(taskArgs.l2token) || + !ethers.utils.isAddress(taskArgs.counterpart) || + !ethers.utils.isAddress(taskArgs.router) || + !ethers.utils.isAddress(taskArgs.messenger) + ) { + console.error(`address params check failed`) + return + } + // deploy gateway impl + const GatewayFactory = await hre.ethers.getContractFactory("L2USDCGateway") + const gateway = await GatewayFactory.deploy(taskArgs.l1token, taskArgs.l2token) + await gateway.deployed() + console.log(`gateway impl deployed at ${gateway.address}`) + + const TransparentProxyFactory = await hre.ethers.getContractFactory("TransparentUpgradeableProxy") + const proxy = await TransparentProxyFactory.deploy( + gateway.address, //logic + taskArgs.proxyadmin, //admin + GatewayFactory.interface.encodeFunctionData("initialize",[ + taskArgs.counterpart, + taskArgs.router, + taskArgs.messenger + ] + ) + ) + await proxy.deployed() + console.log(`gateway proxy deployed at ${proxy.address}`) + + const gatewayProxy = GatewayFactory.attach(proxy.address) + const owner = await gatewayProxy.owner() + const counterpart = await gatewayProxy.counterpart() + const router = await gatewayProxy.router() + const messenger = await gatewayProxy.messenger() + const l1USDC = await gatewayProxy.l1USDC() + const l2USDC = await gatewayProxy.l2USDC() + console.log(`owner ${owner}, gatewayProxy ${gatewayProxy.address}, counterpart ${counterpart}, router ${router}, messenger ${messenger}, l1USDC ${l1USDC}, l2USDC ${l2USDC}`) + }) + +task("deploy-l2-MigrationUSDC") + .addParam("proxyadmin") + .addParam("oldtoken") + .addParam("newtoken") + .setAction(async (taskArgs, hre) => { + // params check + if (!ethers.utils.isAddress(taskArgs.proxyadmin) || + !ethers.utils.isAddress(taskArgs.oldtoken) || + !ethers.utils.isAddress(taskArgs.newtoken) + ) { + console.error(`address params check failed`) + return + } + // deploy gateway impl + const ContractFactory = await hre.ethers.getContractFactory("MigrationUSDC") + const contract = await ContractFactory.deploy(taskArgs.oldtoken, taskArgs.newtoken) + await contract.deployed() + console.log(`MigrationUSDC impl deployed at ${contract.address}`) + + const TransparentProxyFactory = await hre.ethers.getContractFactory("TransparentUpgradeableProxy") + const proxy = await TransparentProxyFactory.deploy( + contract.address, //logic + taskArgs.proxyadmin, //admin + ContractFactory.interface.encodeFunctionData("initialize",[] + ) + ) + await proxy.deployed() + console.log(`MigrationUSDC proxy deployed at ${proxy.address}`) + + const migrationProxy = ContractFactory.attach(proxy.address) + const owner = await migrationProxy.owner() + const oldtoken = await migrationProxy.OLD_USDC() + const newtoken = await migrationProxy.NEW_USDC() + console.log(`owner ${owner}, oldtoken ${oldtoken}, newtoken ${newtoken}`) + }) \ No newline at end of file