Commit 1abdc9e2 by github-actions

Transpile 90bb9c53

parent 34947608
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
* `ERC777`: make reception acquirement optional in `_mint`. ([#2552](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2552)) * `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)) * `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)) * `ERC20FlashMint`: add an implementation of the ERC3156 extension for flash-minting ERC20 tokens. ([#2543](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2543))
* `SignatureChecker`: add a signature verification library that supports both EOA and ERC1271 compliant contracts as signers. ([#2532](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2532))
* `Multicall`: add abstract contract with `multicall(bytes[] calldata data)` function to bundle multiple calls together ([#2608](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2608))
## 4.0.0 (2021-03-23) ## 4.0.0 (2021-03-23)
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*/
interface IERC1271Upgradeable {
/**
* @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 _data
*/
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../access/OwnableUpgradeable.sol";
import "../interfaces/IERC1271Upgradeable.sol";
import "../utils/cryptography/ECDSAUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
contract ERC1271WalletMockUpgradeable is Initializable, OwnableUpgradeable, IERC1271Upgradeable {
function __ERC1271WalletMock_init(address originalOwner) internal initializer {
__Context_init_unchained();
__Ownable_init_unchained();
__ERC1271WalletMock_init_unchained(originalOwner);
}
function __ERC1271WalletMock_init_unchained(address originalOwner) internal initializer {
transferOwnership(originalOwner);
}
function isValidSignature(bytes32 hash, bytes memory signature) public view override returns (bytes4 magicValue) {
return ECDSAUpgradeable.recover(hash, signature) == owner() ? this.isValidSignature.selector : bytes4(0);
}
uint256[50] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./MulticallTokenMockUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
contract MulticallTestUpgradeable is Initializable {
function __MulticallTest_init() internal initializer {
__MulticallTest_init_unchained();
}
function __MulticallTest_init_unchained() internal initializer {
}
function testReturnValues(MulticallTokenMockUpgradeable multicallToken, address[] calldata recipients, uint256[] calldata amounts) external {
bytes[] memory calls = new bytes[](recipients.length);
for (uint i = 0; i < recipients.length; i++) {
calls[i] = abi.encodeWithSignature("transfer(address,uint256)", recipients[i], amounts[i]);
}
bytes[] memory results = multicallToken.multicall(calls);
for (uint i = 0; i < results.length; i++) {
require(abi.decode(results[i], (bool)));
}
}
uint256[50] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/MulticallUpgradeable.sol";
import "./ERC20MockUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
contract MulticallTokenMockUpgradeable is Initializable, ERC20MockUpgradeable, MulticallUpgradeable {
function __MulticallTokenMock_init(uint256 initialBalance) internal initializer {
__Context_init_unchained();
__ERC20_init_unchained(name, symbol);
__ERC20Mock_init_unchained("MulticallToken", "BCT", msg.sender, initialBalance);
__Multicall_init_unchained();
__MulticallTokenMock_init_unchained(initialBalance);
}
function __MulticallTokenMock_init_unchained(uint256 initialBalance) internal initializer {}
uint256[50] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/cryptography/SignatureCheckerUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
contract SignatureCheckerMockUpgradeable is Initializable {
function __SignatureCheckerMock_init() internal initializer {
__SignatureCheckerMock_init_unchained();
}
function __SignatureCheckerMock_init_unchained() internal initializer {
}
using SignatureCheckerUpgradeable for address;
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) public view returns (bool) {
return signer.isValidSignatureNow(hash, signature);
}
uint256[50] private __gap;
}
...@@ -160,6 +160,53 @@ contract OwnableMockUpgradeableWithInit is OwnableMockUpgradeable { ...@@ -160,6 +160,53 @@ contract OwnableMockUpgradeableWithInit is OwnableMockUpgradeable {
__OwnableMock_init(); __OwnableMock_init();
} }
} }
import "./ERC1271WalletMockUpgradeable.sol";
contract ERC1271WalletMockUpgradeableWithInit is ERC1271WalletMockUpgradeable {
constructor(address originalOwner) public payable {
__ERC1271WalletMock_init(originalOwner);
}
}
import "./SignatureCheckerMockUpgradeable.sol";
contract SignatureCheckerMockUpgradeableWithInit is SignatureCheckerMockUpgradeable {
constructor() public payable {
__SignatureCheckerMock_init();
}
}
import "./EIP712ExternalUpgradeable.sol";
contract EIP712ExternalUpgradeableWithInit is EIP712ExternalUpgradeable {
constructor(string memory name, string memory version) public payable {
__EIP712External_init(name, version);
}
}
import "../metatx/MinimalForwarderUpgradeable.sol";
contract MinimalForwarderUpgradeableWithInit is MinimalForwarderUpgradeable {
constructor() public payable {
__MinimalForwarder_init();
}
}
import "./ERC20PermitMockUpgradeable.sol";
contract ERC20PermitMockUpgradeableWithInit is ERC20PermitMockUpgradeable {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable {
__ERC20PermitMock_init(name, symbol, initialAccount, initialBalance);
}
}
import "./ECDSAMockUpgradeable.sol";
contract ECDSAMockUpgradeableWithInit is ECDSAMockUpgradeable {
constructor() public payable {
__ECDSAMock_init();
}
}
import "../utils/escrow/EscrowUpgradeable.sol"; import "../utils/escrow/EscrowUpgradeable.sol";
contract EscrowUpgradeableWithInit is EscrowUpgradeable { contract EscrowUpgradeableWithInit is EscrowUpgradeable {
...@@ -307,6 +354,32 @@ contract ERC20FlashMintMockUpgradeableWithInit is ERC20FlashMintMockUpgradeable ...@@ -307,6 +354,32 @@ contract ERC20FlashMintMockUpgradeableWithInit is ERC20FlashMintMockUpgradeable
__ERC20FlashMintMock_init(name, symbol, initialAccount, initialBalance); __ERC20FlashMintMock_init(name, symbol, initialAccount, initialBalance);
} }
} }
import "./MulticallTokenMockUpgradeable.sol";
contract MulticallTokenMockUpgradeableWithInit is MulticallTokenMockUpgradeable {
constructor(uint256 initialBalance) public payable {
__MulticallTokenMock_init(initialBalance);
}
}
import "./ERC20MockUpgradeable.sol";
contract ERC20MockUpgradeableWithInit is ERC20MockUpgradeable {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable {
__ERC20Mock_init(name, symbol, initialAccount, initialBalance);
}
}
import "./MulticallTestUpgradeable.sol";
contract MulticallTestUpgradeableWithInit is MulticallTestUpgradeable {
constructor() public payable {
__MulticallTest_init();
}
}
import "./ClonesMockUpgradeable.sol"; import "./ClonesMockUpgradeable.sol";
contract ClonesMockUpgradeableWithInit is ClonesMockUpgradeable { contract ClonesMockUpgradeableWithInit is ClonesMockUpgradeable {
...@@ -462,51 +535,6 @@ contract ERC2771ContextMockUpgradeableWithInit is ERC2771ContextMockUpgradeable ...@@ -462,51 +535,6 @@ contract ERC2771ContextMockUpgradeableWithInit is ERC2771ContextMockUpgradeable
__ERC2771ContextMock_init(trustedForwarder); __ERC2771ContextMock_init(trustedForwarder);
} }
} }
import "./EIP712ExternalUpgradeable.sol";
contract EIP712ExternalUpgradeableWithInit is EIP712ExternalUpgradeable {
constructor(string memory name, string memory version) public payable {
__EIP712External_init(name, version);
}
}
import "../metatx/MinimalForwarderUpgradeable.sol";
contract MinimalForwarderUpgradeableWithInit is MinimalForwarderUpgradeable {
constructor() public payable {
__MinimalForwarder_init();
}
}
import "./ECDSAMockUpgradeable.sol";
contract ECDSAMockUpgradeableWithInit is ECDSAMockUpgradeable {
constructor() public payable {
__ECDSAMock_init();
}
}
import "./ERC20PermitMockUpgradeable.sol";
contract ERC20PermitMockUpgradeableWithInit is ERC20PermitMockUpgradeable {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable {
__ERC20PermitMock_init(name, symbol, initialAccount, initialBalance);
}
}
import "./ERC20MockUpgradeable.sol";
contract ERC20MockUpgradeableWithInit is ERC20MockUpgradeable {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable {
__ERC20Mock_init(name, symbol, initialAccount, initialBalance);
}
}
import "./ERC20DecimalsMockUpgradeable.sol"; import "./ERC20DecimalsMockUpgradeable.sol";
contract ERC20DecimalsMockUpgradeableWithInit is ERC20DecimalsMockUpgradeable { contract ERC20DecimalsMockUpgradeableWithInit is ERC20DecimalsMockUpgradeable {
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./AddressUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Provides a function to batch together multiple calls in a single external call.
*/
abstract contract MulticallUpgradeable is Initializable {
function __Multicall_init() internal initializer {
__Multicall_init_unchained();
}
function __Multicall_init_unchained() internal initializer {
}
/**
* @dev Receives and executes a batch of function calls on this contract.
*/
function multicall(bytes[] calldata data) external returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint i = 0; i < data.length; i++) {
results[i] = AddressUpgradeable.functionDelegateCall(address(this), data[i]);
}
return results;
}
uint256[50] private __gap;
}
...@@ -6,6 +6,7 @@ NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/ ...@@ -6,6 +6,7 @@ NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/
Miscellaneous contracts and libraries containing utility functions you can use to improve security, work with new data types, or safely use low-level primitives. Miscellaneous contracts and libraries containing utility functions you can use to improve security, work with new data types, or safely use low-level primitives.
The {Address}, {Arrays} and {Strings} libraries provide more operations related to these native data types, while {SafeCast} adds ways to safely convert between the different signed and unsigned numeric types. The {Address}, {Arrays} and {Strings} libraries provide more operations related to these native data types, while {SafeCast} adds ways to safely convert between the different signed and unsigned numeric types.
{Multicall} provides a function to batch together multiple calls in a single external call.
For new data types: For new data types:
...@@ -36,6 +37,8 @@ Finally, {Create2} contains all necessary utilities to safely use the https://bl ...@@ -36,6 +37,8 @@ Finally, {Create2} contains all necessary utilities to safely use the https://bl
{{ECDSA}} {{ECDSA}}
{{SignatureChecker}}
{{MerkleProof}} {{MerkleProof}}
{{EIP712}} {{EIP712}}
...@@ -92,3 +95,5 @@ Note that, in all cases, accounts simply _declare_ their interfaces, but they ar ...@@ -92,3 +95,5 @@ Note that, in all cases, accounts simply _declare_ their interfaces, but they ar
{{Counters}} {{Counters}}
{{Strings}} {{Strings}}
{{Multicall}}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./ECDSAUpgradeable.sol";
import "../AddressUpgradeable.sol";
import "../../interfaces/IERC1271Upgradeable.sol";
/**
* @dev Signature verification helper: Provide a single mechanism to verify both private-key (EOA) ECDSA signature and
* ERC1271 contract sigantures. Using this instead of ECDSA.recover in your contract will make them compatible with
* smart contract wallets such as Argent and Gnosis.
*
* Note: unlike ECDSA signatures, contract signature's 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).
*/
library SignatureCheckerUpgradeable {
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
if (AddressUpgradeable.isContract(signer)) {
try IERC1271Upgradeable(signer).isValidSignature(hash, signature) returns (bytes4 magicValue) {
return magicValue == IERC1271Upgradeable(signer).isValidSignature.selector;
} catch {
return false;
}
} else {
return ECDSAUpgradeable.recover(hash, signature) == signer;
}
}
}
...@@ -99,3 +99,41 @@ Want to check if an address is a contract? Use xref:api:utils.adoc#Address[`Addr ...@@ -99,3 +99,41 @@ Want to check if an address is a contract? Use xref:api:utils.adoc#Address[`Addr
Want to keep track of some numbers that increment by 1 every time you want another one? Check out xref:api:utils.adoc#Counters[`Counters`]. This is useful for lots of things, like creating incremental identifiers, as shown on the xref:erc721.adoc[ERC721 guide]. Want to keep track of some numbers that increment by 1 every time you want another one? Check out xref:api:utils.adoc#Counters[`Counters`]. This is useful for lots of things, like creating incremental identifiers, as shown on the xref:erc721.adoc[ERC721 guide].
=== Multicall
The `Multicall` abstract contract comes with a `multicall` function that bundles together multiple calls in a single external call. With it, external accounts may perform atomic operations comprising several function calls. This is not only useful for EOAs to make multiple calls in a single transaction, it's also a way to revert a previous call if a later one fails.
Consider this dummy contract:
[source,solidity]
----
// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/Multicall.sol";
contract Box is Multicall {
function foo() public {
...
}
function bar() public {
...
}
}
----
This is how to call the `multicall` function using Truffle, allowing `foo` and `bar` to be called in a single transaction:
[source,javascript]
----
// scripts/foobar.js
const Box = artifacts.require('Box');
const instance = await Box.new();
await instance.multicall([
instance.contract.methods.foo().encodeABI(),
instance.contract.methods.bar().encodeABI()
]);
----
const { BN, expectRevert } = require('@openzeppelin/test-helpers');
const MulticallTokenMock = artifacts.require('MulticallTokenMock');
contract('MulticallToken', function (accounts) {
const [deployer, alice, bob] = accounts;
const amount = 12000;
beforeEach(async function () {
this.multicallToken = await MulticallTokenMock.new(new BN(amount), { from: deployer });
});
it('batches function calls', async function () {
expect(await this.multicallToken.balanceOf(alice)).to.be.bignumber.equal(new BN('0'));
expect(await this.multicallToken.balanceOf(bob)).to.be.bignumber.equal(new BN('0'));
await this.multicallToken.multicall([
this.multicallToken.contract.methods.transfer(alice, amount / 2).encodeABI(),
this.multicallToken.contract.methods.transfer(bob, amount / 3).encodeABI(),
], { from: deployer });
expect(await this.multicallToken.balanceOf(alice)).to.be.bignumber.equal(new BN(amount / 2));
expect(await this.multicallToken.balanceOf(bob)).to.be.bignumber.equal(new BN(amount / 3));
});
it('returns an array with the result of each call', async function () {
const MulticallTest = artifacts.require('MulticallTest');
const multicallTest = await MulticallTest.new({ from: deployer });
await this.multicallToken.transfer(multicallTest.address, amount, { from: deployer });
expect(await this.multicallToken.balanceOf(multicallTest.address)).to.be.bignumber.equal(new BN(amount));
const recipients = [alice, bob];
const amounts = [amount / 2, amount / 3].map(n => new BN(n));
await multicallTest.testReturnValues(this.multicallToken.address, recipients, amounts);
});
it('reverts previous calls', async function () {
expect(await this.multicallToken.balanceOf(alice)).to.be.bignumber.equal(new BN('0'));
const call = this.multicallToken.multicall([
this.multicallToken.contract.methods.transfer(alice, amount).encodeABI(),
this.multicallToken.contract.methods.transfer(bob, amount).encodeABI(),
], { from: deployer });
await expectRevert(call, 'ERC20: transfer amount exceeds balance');
expect(await this.multicallToken.balanceOf(alice)).to.be.bignumber.equal(new BN('0'));
});
it('bubbles up revert reasons', async function () {
const call = this.multicallToken.multicall([
this.multicallToken.contract.methods.transfer(alice, amount).encodeABI(),
this.multicallToken.contract.methods.transfer(bob, amount).encodeABI(),
], { from: deployer });
await expectRevert(call, 'ERC20: transfer amount exceeds balance');
});
});
const { toEthSignedMessageHash, fixSignature } = require('../../helpers/sign');
const { expect } = require('chai');
const SignatureCheckerMock = artifacts.require('SignatureCheckerMock');
const ERC1271WalletMock = artifacts.require('ERC1271WalletMock');
const TEST_MESSAGE = web3.utils.sha3('OpenZeppelin');
const WRONG_MESSAGE = web3.utils.sha3('Nope');
contract('SignatureChecker (ERC1271)', function (accounts) {
const [signer, other] = accounts;
before('deploying', async function () {
this.signaturechecker = await SignatureCheckerMock.new();
this.wallet = await ERC1271WalletMock.new(signer);
this.signature = fixSignature(await web3.eth.sign(TEST_MESSAGE, signer));
});
context('EOA account', function () {
it('with matching signer and signature', async function () {
expect(await this.signaturechecker.isValidSignatureNow(
signer,
toEthSignedMessageHash(TEST_MESSAGE),
this.signature,
)).to.equal(true);
});
it('with invalid signer', async function () {
expect(await this.signaturechecker.isValidSignatureNow(
other,
toEthSignedMessageHash(TEST_MESSAGE),
this.signature,
)).to.equal(false);
});
it('with invalid signature', async function () {
expect(await this.signaturechecker.isValidSignatureNow(
signer,
toEthSignedMessageHash(WRONG_MESSAGE),
this.signature,
)).to.equal(false);
});
});
context('ERC1271 wallet', function () {
it('with matching signer and signature', async function () {
expect(await this.signaturechecker.isValidSignatureNow(
this.wallet.address,
toEthSignedMessageHash(TEST_MESSAGE),
this.signature,
)).to.equal(true);
});
it('with invalid signer', async function () {
expect(await this.signaturechecker.isValidSignatureNow(
this.signaturechecker.address,
toEthSignedMessageHash(TEST_MESSAGE),
this.signature,
)).to.equal(false);
});
it('with invalid signature', async function () {
expect(await this.signaturechecker.isValidSignatureNow(
this.wallet.address,
toEthSignedMessageHash(WRONG_MESSAGE),
this.signature,
)).to.equal(false);
});
});
});
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