Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add validation in Governor on ERC-721 or ERC-1155 received #4314

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/mighty-donuts-smile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': patch
---

Add validation in Governor ERC1155 and ERC721 receiver hooks to ensure Governor is the executor
9 changes: 6 additions & 3 deletions contracts/governance/Governor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -603,14 +603,15 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor, IERC721Receive
}

/**
* @dev See {IERC721Receiver-onERC721Received}.
* @dev See {IERC721Receiver-onERC721Received}(disabled if executor is a third party contract).
Amxx marked this conversation as resolved.
Show resolved Hide resolved
*/
function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) {
require(_executor() == address(this), "Governor: must send to executor");
return this.onERC721Received.selector;
}

/**
* @dev See {IERC1155Receiver-onERC1155Received}.
* @dev See {IERC1155Receiver-onERC1155Received}(disabled if executor is a third party contract).
Amxx marked this conversation as resolved.
Show resolved Hide resolved
*/
function onERC1155Received(
address,
Expand All @@ -619,11 +620,12 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor, IERC721Receive
uint256,
bytes memory
) public virtual override returns (bytes4) {
require(_executor() == address(this), "Governor: must send to executor");
return this.onERC1155Received.selector;
}

/**
* @dev See {IERC1155Receiver-onERC1155BatchReceived}.
* @dev See {IERC1155Receiver-onERC1155BatchReceived}(disabled if executor is a third party contract).
Amxx marked this conversation as resolved.
Show resolved Hide resolved
*/
function onERC1155BatchReceived(
address,
Expand All @@ -632,6 +634,7 @@ abstract contract Governor is Context, ERC165, EIP712, IGovernor, IERC721Receive
uint256[] memory,
bytes memory
) public virtual override returns (bytes4) {
require(_executor() == address(this), "Governor: must send to executor");
return this.onERC1155BatchReceived.selector;
}
}
60 changes: 60 additions & 0 deletions test/governance/extensions/GovernorTimelockCompound.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsI
const Timelock = artifacts.require('CompTimelock');
const Governor = artifacts.require('$GovernorTimelockCompoundMock');
const CallReceiver = artifacts.require('CallReceiverMock');
const ERC721 = artifacts.require('$ERC721');
const ERC1155 = artifacts.require('$ERC1155');

function makeContractAddress(creator, nonce) {
return web3.utils.toChecksumAddress(
Expand Down Expand Up @@ -202,6 +204,64 @@ contract('GovernorTimelockCompound', function (accounts) {
await expectRevert(this.helper.execute(), 'Governor: proposal not successful');
});
});

describe('on safe receive', function () {
describe('ERC721', function () {
const name = 'Non Fungible Token';
const symbol = 'NFT';
const tokenId = web3.utils.toBN(1);

beforeEach(async function () {
this.token = await ERC721.new(name, symbol);
await this.token.$_mint(owner, tokenId);
});

it("can't receive an ERC721 safeTransfer", async function () {
await expectRevert.unspecified(
this.token.safeTransferFrom(owner, this.mock.address, tokenId, { from: owner }),
);
});
});

describe('ERC1155', function () {
const uri = 'https://token-cdn-domain/{id}.json';
const tokenIds = {
1: web3.utils.toBN(1000),
2: web3.utils.toBN(2000),
3: web3.utils.toBN(3000),
};

beforeEach(async function () {
this.token = await ERC1155.new(uri);
await this.token.$_mintBatch(owner, Object.keys(tokenIds), Object.values(tokenIds), '0x');
});

it("can't receive ERC1155 safeTransfer", async function () {
await expectRevert.unspecified(
this.token.safeTransferFrom(
owner,
this.mock.address,
...Object.entries(tokenIds)[0], // id + amount
'0x',
{ from: owner },
),
);
});

it("can't receive ERC1155 safeBatchTransfer", async function () {
await expectRevert.unspecified(
this.token.safeBatchTransferFrom(
owner,
this.mock.address,
Object.keys(tokenIds),
Object.values(tokenIds),
'0x',
{ from: owner },
),
);
});
});
});
});

describe('cancel', function () {
Expand Down
60 changes: 60 additions & 0 deletions test/governance/extensions/GovernorTimelockControl.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsI
const Timelock = artifacts.require('TimelockController');
const Governor = artifacts.require('$GovernorTimelockControlMock');
const CallReceiver = artifacts.require('CallReceiverMock');
const ERC721 = artifacts.require('$ERC721');
const ERC1155 = artifacts.require('$ERC1155');

const TOKENS = [
{ Token: artifacts.require('$ERC20Votes'), mode: 'blocknumber' },
Expand Down Expand Up @@ -439,6 +441,64 @@ contract('GovernorTimelockControl', function (accounts) {
// then coverage reports.
});
});

describe('on safe receive', function () {
describe('ERC721', function () {
const name = 'Non Fungible Token';
const symbol = 'NFT';
const tokenId = web3.utils.toBN(1);

beforeEach(async function () {
this.token = await ERC721.new(name, symbol);
await this.token.$_mint(owner, tokenId);
});

it("can't receive an ERC721 safeTransfer", async function () {
await expectRevert.unspecified(
this.token.safeTransferFrom(owner, this.mock.address, tokenId, { from: owner }),
);
});
});

describe('ERC1155', function () {
const uri = 'https://token-cdn-domain/{id}.json';
const tokenIds = {
1: web3.utils.toBN(1000),
2: web3.utils.toBN(2000),
3: web3.utils.toBN(3000),
};

beforeEach(async function () {
this.token = await ERC1155.new(uri);
await this.token.$_mintBatch(owner, Object.keys(tokenIds), Object.values(tokenIds), '0x');
});

it("can't receive ERC1155 safeTransfer", async function () {
await expectRevert.unspecified(
this.token.safeTransferFrom(
owner,
this.mock.address,
...Object.entries(tokenIds)[0], // id + amount
'0x',
{ from: owner },
),
);
});

it("can't receive ERC1155 safeBatchTransfer", async function () {
await expectRevert.unspecified(
this.token.safeBatchTransferFrom(
owner,
this.mock.address,
Object.keys(tokenIds),
Object.values(tokenIds),
'0x',
{ from: owner },
),
);
});
});
});
});
});
}
Expand Down