Commit 34947608 by github-actions

Transpile 39dab056

parent 64bf0c74
......@@ -5,6 +5,7 @@
* `IERC20Metadata`: add a new extended interface that includes the optional `name()`, `symbol()` and `decimals()` functions. ([#2561](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2561))
* `ERC777`: make reception acquirement optional in `_mint`. ([#2552](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2552))
* `ERC20Permit`: add a `_useNonce` to enable further usage of ERC712 signatures. ([#2565](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2565))
* `ERC20FlashMint`: add an implementation of the ERC3156 extension for flash-minting ERC20 tokens. ([#2543](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2543))
## 4.0.0 (2021-03-23)
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC3156 FlashBorrower, as defined in
* https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
*/
interface IERC3156FlashBorrowerUpgradeable {
/**
* @dev Receive a flash loan.
* @param initiator The initiator of the loan.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @param fee The additional amount of tokens to repay.
* @param data Arbitrary data structure, intended to contain user-defined parameters.
* @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan"
*/
function onFlashLoan(
address initiator,
address token,
uint256 amount,
uint256 fee,
bytes calldata data
) external returns (bytes32);
}
/**
* @dev Interface of the ERC3156 FlashLender, as defined in
* https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
*/
interface IERC3156FlashLenderUpgradeable {
/**
* @dev The amount of currency available to be lended.
* @param token The loan currency.
* @return The amount of `token` that can be borrowed.
*/
function maxFlashLoan(
address token
) external view returns (uint256);
/**
* @dev The fee to be charged for a given loan.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @return The amount of `token` to be charged for the loan, on top of the returned principal.
*/
function flashFee(
address token,
uint256 amount
) external view returns (uint256);
/**
* @dev Initiate a flash loan.
* @param receiver The receiver of the tokens in the loan, and the receiver of the callback.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @param data Arbitrary data structure, intended to contain user-defined parameters.
*/
function flashLoan(
IERC3156FlashBorrowerUpgradeable receiver,
address token,
uint256 amount,
bytes calldata data
) external returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../token/ERC20/IERC20Upgradeable.sol";
import "../interfaces/IERC3156Upgradeable.sol";
import "../utils/AddressUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev WARNING: this IERC3156FlashBorrower mock implementation is for testing purposes ONLY.
* Writing a secure flash lock borrower is not an easy task, and should be done with the utmost care.
* This is not an example of how it should be done, and no pattern present in this mock should be considered secure.
* Following best practices, always have your contract properly audited before using them to manipulate important funds on
* live networks.
*/
contract ERC3156FlashBorrowerMockUpgradeable is Initializable, IERC3156FlashBorrowerUpgradeable {
bytes32 constant internal RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan");
bool _enableApprove;
bool _enableReturn;
event BalanceOf(address token, address account, uint256 value);
event TotalSupply(address token, uint256 value);
function __ERC3156FlashBorrowerMock_init(bool enableReturn, bool enableApprove) internal initializer {
__ERC3156FlashBorrowerMock_init_unchained(enableReturn, enableApprove);
}
function __ERC3156FlashBorrowerMock_init_unchained(bool enableReturn, bool enableApprove) internal initializer {
_enableApprove = enableApprove;
_enableReturn = enableReturn;
}
function onFlashLoan(
address /*initiator*/,
address token,
uint256 amount,
uint256 fee,
bytes calldata data
) public override returns (bytes32) {
require(msg.sender == token);
emit BalanceOf(token, address(this), IERC20Upgradeable(token).balanceOf(address(this)));
emit TotalSupply(token, IERC20Upgradeable(token).totalSupply());
if (data.length > 0) {
// WARNING: This code is for testing purposes only! Do not use.
AddressUpgradeable.functionCall(token, data);
}
if (_enableApprove) {
IERC20Upgradeable(token).approve(token, amount + fee);
}
return _enableReturn ? RETURN_VALUE : bytes32(0);
}
uint256[50] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../token/ERC20/extensions/draft-ERC20FlashMintUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
contract ERC20FlashMintMockUpgradeable is Initializable, ERC20FlashMintUpgradeable {
function __ERC20FlashMintMock_init(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) internal initializer {
__Context_init_unchained();
__ERC20_init_unchained(name, symbol);
__ERC20FlashMint_init_unchained();
__ERC20FlashMintMock_init_unchained(name, symbol, initialAccount, initialBalance);
}
function __ERC20FlashMintMock_init_unchained(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) internal initializer {
_mint(initialAccount, initialBalance);
}
uint256[50] private __gap;
}
......@@ -288,6 +288,25 @@ contract ERC777MockUpgradeableWithInit is ERC777MockUpgradeable {
__ERC777Mock_init(initialHolder, initialBalance, name, symbol, defaultOperators);
}
}
import "./ERC3156FlashBorrowerMockUpgradeable.sol";
contract ERC3156FlashBorrowerMockUpgradeableWithInit is ERC3156FlashBorrowerMockUpgradeable {
constructor(bool enableReturn, bool enableApprove) public payable {
__ERC3156FlashBorrowerMock_init(enableReturn, enableApprove);
}
}
import "./ERC3156MockUpgradeable.sol";
contract ERC20FlashMintMockUpgradeableWithInit is ERC20FlashMintMockUpgradeable {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable {
__ERC20FlashMintMock_init(name, symbol, initialAccount, initialBalance);
}
}
import "./ClonesMockUpgradeable.sol";
contract ClonesMockUpgradeableWithInit is ClonesMockUpgradeable {
......
......@@ -15,21 +15,22 @@ There a few core contracts that implement the behavior specified in the EIP:
Additionally there are multiple custom extensions, including:
* {ERC20Permit}: gasless approval of tokens.
* {ERC20Snapshot}: efficient storage of past token balances to be later queried at any point in time.
* {ERC20Burnable}: destruction of own tokens.
* {ERC20Capped}: enforcement of a cap to the total supply when minting tokens.
* {ERC20Pausable}: ability to pause token transfers.
* {ERC20Snapshot}: efficient storage of past token balances to be later queried at any point in time.
* {ERC20Permit}: gasless approval of tokens (standardized as ERC2612).
* {ERC20FlashMint}: token level support for flash loans through the minting and burning of ephemeral tokens (standardized as ERC3156).
Finally, there are some utilities to interact with ERC20 contracts in various ways.
* {SafeERC20}: a wrapper around the interface that eliminates the need to handle boolean return values.
* {TokenTimelock}: hold tokens for a beneficiary until a specified time.
The following related EIPs are in draft status and can be found in the drafts directory.
The following related EIPs are in draft status.
- {IERC20Permit}
- {ERC20Permit}
- {ERC20FlashMint}
NOTE: This core set of contracts is designed to be unopinionated, allowing developers to access the internal functions in ERC20 (such as <<ERC20-_mint-address-uint256-,`_mint`>>) and expose them as external functions in the way they prefer. On the other hand, xref:ROOT:erc20.adoc#Presets[ERC20 Presets] (such as {ERC20PresetMinterPauser}) are designed using opinionated patterns to provide developers with ready to use, deployable contracts.
......@@ -43,22 +44,22 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel
== Extensions
{{ERC20Snapshot}}
{{ERC20Pausable}}
{{ERC20Burnable}}
{{ERC20Capped}}
{{ERC20Pausable}}
{{ERC20Snapshot}}
== Draft EIPs
The following EIPs are still in Draft status. Due to their nature as drafts, the details of these contracts may change and we cannot guarantee their xref:ROOT:releases-stability.adoc[stability]. Minor releases of OpenZeppelin Contracts may contain breaking changes for the contracts in this directory, which will be duly announced in the https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CHANGELOG.md[changelog]. The EIPs included here are used by projects in production and this may make them less likely to change significantly.
{{IERC20Permit}}
{{ERC20Permit}}
{{ERC20FlashMint}}
== Presets
These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code.
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../../../interfaces/IERC3156Upgradeable.sol";
import "../ERC20Upgradeable.sol";
import "../../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the ERC3156 Flash loans extension, as defined in
* https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
*
* Adds the {flashLoan} method, which provides flash loan support at the token
* level. By default there is no fee, but this can be changed by overriding {flashFee}.
*/
abstract contract ERC20FlashMintUpgradeable is Initializable, ERC20Upgradeable, IERC3156FlashLenderUpgradeable {
function __ERC20FlashMint_init() internal initializer {
__Context_init_unchained();
__ERC20FlashMint_init_unchained();
}
function __ERC20FlashMint_init_unchained() internal initializer {
}
bytes32 constant private RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan");
/**
* @dev Returns the maximum amount of tokens available for loan.
* @param token The address of the token that is requested.
* @return The amont of token that can be loaned.
*/
function maxFlashLoan(address token) public view override returns (uint256) {
return token == address(this) ? type(uint256).max - ERC20Upgradeable.totalSupply() : 0;
}
/**
* @dev Returns the fee applied when doing flash loans. By default this
* implementation has 0 fees. This function can be overloaded to make
* the flash loan mechanism deflationary.
* @param token The token to be flash loaned.
* @param amount The amount of tokens to be loaned.
* @return The fees applied to the corresponding flash loan.
*/
function flashFee(address token, uint256 amount) public view virtual override returns (uint256) {
require(token == address(this), "ERC20FlashMint: wrong token");
// silence warning about unused variable without the addition of bytecode.
amount;
return 0;
}
/**
* @dev Performs a flash loan. New tokens are minted and sent to the
* `receiver`, who is required to implement the {IERC3156FlashBorrower}
* interface. By the end of the flash loan, the receiver is expected to own
* amount + fee tokens and have them approved back to the token contract itself so
* they can be burned.
* @param receiver The receiver of the flash loan. Should implement the
* {IERC3156FlashBorrower.onFlashLoan} interface.
* @param token The token to be flash loaned. Only `address(this)` is
* supported.
* @param amount The amount of tokens to be loaned.
* @param data An arbitrary datafield that is passed to the receiver.
* @return `true` is the flash loan was successfull.
*/
function flashLoan(
IERC3156FlashBorrowerUpgradeable receiver,
address token,
uint256 amount,
bytes calldata data
)
public virtual override returns (bool)
{
uint256 fee = flashFee(token, amount);
_mint(address(receiver), amount);
require(receiver.onFlashLoan(msg.sender, token, amount, fee, data) == RETURN_VALUE, "ERC20FlashMint: invalid return value");
uint256 currentAllowance = allowance(address(receiver), address(this));
require(currentAllowance >= amount + fee, "ERC20FlashMint: allowance does not allow refund");
_approve(address(receiver), address(this), currentAllowance - amount - fee);
_burn(address(receiver), amount + fee);
return true;
}
uint256[50] private __gap;
}
* xref:index.adoc[Overview]
* xref:wizard.adoc[Wizard]
* xref:extending-contracts.adoc[Extending Contracts]
* xref:upgradeable.adoc[Using with Upgrades]
......
= Contracts Wizard
:page-notoc:
Not sure where to start? Use the interactive generator below to bootstrap your
contract and learn about the components offered in OpenZeppelin Contracts.
TIP: Place the resulting contract in your `contracts` directory in order to compile it with a tool like Hardhat or Truffle. Consider reading our guide on xref:learn::developing-smart-contracts.adoc[Developing Smart Contracts] for more guidance!
++++
<script async defer src="https://openzeppelin-contracts-wizard.netlify.app/build/embed.js"></script>
<oz-wizard style="display: block; min-height: 40rem;"></oz-wizard>
++++
/* eslint-disable */
const { BN, constants, expectEvent, expectRevert, time } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const { MAX_UINT256, ZERO_ADDRESS, ZERO_BYTES32 } = constants;
const ERC20FlashMintMock = artifacts.require('ERC20FlashMintMock');
const ERC3156FlashBorrowerMock = artifacts.require('ERC3156FlashBorrowerMock');
contract('ERC20FlashMint', function (accounts) {
const [ initialHolder, other ] = accounts;
const name = 'My Token';
const symbol = 'MTKN';
const initialSupply = new BN(100);
const loanAmount = new BN(10000000000000);
beforeEach(async function () {
this.token = await ERC20FlashMintMock.new(name, symbol, initialHolder, initialSupply);
});
describe('maxFlashLoan', function () {
it('token match', async function () {
expect(await this.token.maxFlashLoan(this.token.address)).to.be.bignumber.equal(MAX_UINT256.sub(initialSupply));
});
it('token mismatch', async function () {
expect(await this.token.maxFlashLoan(ZERO_ADDRESS)).to.be.bignumber.equal('0');
});
});
describe('flashFee', function () {
it('token match', async function () {
expect(await this.token.flashFee(this.token.address, loanAmount)).to.be.bignumber.equal('0');
});
it('token mismatch', async function () {
await expectRevert(this.token.flashFee(ZERO_ADDRESS, loanAmount), 'ERC20FlashMint: wrong token');
});
});
describe('flashLoan', function () {
it('success', async function () {
const receiver = await ERC3156FlashBorrowerMock.new(true, true);
const { tx } = await this.token.flashLoan(receiver.address, this.token.address, loanAmount, '0x');
expectEvent.inTransaction(tx, this.token, 'Transfer', { from: ZERO_ADDRESS, to: receiver.address, value: loanAmount });
expectEvent.inTransaction(tx, this.token, 'Transfer', { from: receiver.address, to: ZERO_ADDRESS, value: loanAmount });
expectEvent.inTransaction(tx, receiver, 'BalanceOf', { token: this.token.address, account: receiver.address, value: loanAmount });
expectEvent.inTransaction(tx, receiver, 'TotalSupply', { token: this.token.address, value: initialSupply.add(loanAmount) });
expect(await this.token.totalSupply()).to.be.bignumber.equal(initialSupply);
expect(await this.token.balanceOf(receiver.address)).to.be.bignumber.equal('0');
expect(await this.token.allowance(receiver.address, this.token.address)).to.be.bignumber.equal('0');
});
it ('missing return value', async function () {
const receiver = await ERC3156FlashBorrowerMock.new(false, true);
await expectRevert(
this.token.flashLoan(receiver.address, this.token.address, loanAmount, '0x'),
'ERC20FlashMint: invalid return value',
);
});
it ('missing approval', async function () {
const receiver = await ERC3156FlashBorrowerMock.new(true, false);
await expectRevert(
this.token.flashLoan(receiver.address, this.token.address, loanAmount, '0x'),
'ERC20FlashMint: allowance does not allow refund',
);
});
it ('unavailable funds', async function () {
const receiver = await ERC3156FlashBorrowerMock.new(true, true);
const data = this.token.contract.methods.transfer(other, 10).encodeABI();
await expectRevert(
this.token.flashLoan(receiver.address, this.token.address, loanAmount, data),
'ERC20: burn amount exceeds balance',
);
});
it ('more than maxFlashLoan', async function () {
const receiver = await ERC3156FlashBorrowerMock.new(true, true);
const data = this.token.contract.methods.transfer(other, 10).encodeABI();
// _mint overflow reverts using a panic code. No reason string.
await expectRevert.unspecified(this.token.flashLoan(receiver.address, this.token.address, MAX_UINT256, data));
});
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment