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 onERC721Received support #300

Merged
merged 2 commits into from
Jul 12, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
12 changes: 11 additions & 1 deletion contracts/prize-pool/PrizePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/SafeCastUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/introspection/ERC165CheckerUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol";
import "@pooltogether/fixed-point/contracts/FixedPoint.sol";
Expand All @@ -23,7 +24,7 @@ import "./PrizePoolInterface.sol";
/// @title Escrows assets and deposits them into a yield source. Exposes interest to Prize Strategy. Users deposit and withdraw from this contract to participate in Prize Pool.
/// @notice Accounting is managed using Controlled Tokens, whose mint and burn functions can only be called by this contract.
/// @dev Must be inherited to provide specific yield-bearing asset control, such as Compound cTokens
abstract contract PrizePool is PrizePoolInterface, OwnableUpgradeable, ReentrancyGuardUpgradeable, TokenControllerInterface {
abstract contract PrizePool is PrizePoolInterface, OwnableUpgradeable, ReentrancyGuardUpgradeable, TokenControllerInterface, IERC721ReceiverUpgradeable {
using SafeMathUpgradeable for uint256;
using SafeCastUpgradeable for uint256;
using SafeERC20Upgradeable for IERC20Upgradeable;
Expand Down Expand Up @@ -1050,6 +1051,15 @@ abstract contract PrizePool is PrizePoolInterface, OwnableUpgradeable, Reentranc
compLike.delegate(to);
}
}

/// @notice Required for ERC721 safe token transfers from smart contracts.
/// @param operator The address that acts on behalf of the owner
/// @param from The current owner of the NFT
/// @param tokenId The NFT to transfer
/// @param data Additional data with no specified format, sent in call to `_to`.
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external override returns (bytes4){
return IERC721ReceiverUpgradeable.onERC721Received.selector;
}

/// @notice The total of all controlled tokens and timelock.
/// @return The current total of all tokens and timelock.
Expand Down
15 changes: 15 additions & 0 deletions contracts/test/NFT.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
pragma solidity 0.6.12;
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";

contract NFT is ERC721Upgradeable {
function initialize (
string memory name_, string memory symbol_
) external initializer {
__ERC721_init(name_, symbol_);
_safeMint(msg.sender, 0);
}

function simulateSafeTransferFrom(address from, address to, uint256 tokenId) public {
ERC721Upgradeable.safeTransferFrom(from, to, tokenId);
}
}
29 changes: 28 additions & 1 deletion test/PrizePool.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe('PrizePool', function() {
let poolMaxExitFee = toWei('0.5')
let poolMaxTimelockDuration = 10000

let ticket, sponsorship
let ticket, sponsorship, nft

let compLike

Expand Down Expand Up @@ -963,4 +963,31 @@ describe('PrizePool', function() {
.withArgs(wallet.address, erc721token.address, [NFT_TOKEN_ID])
})
})

describe('onERC721Received()', () => {
beforeEach(async () => {
await prizePool.initializeAll(
prizeStrategy.address,
[ticket.address],
poolMaxExitFee,
poolMaxTimelockDuration,
yieldSourceStub.address
)
await prizePool.setPrizeStrategy(prizeStrategy.address)
await prizePool.setCreditPlanOf(ticket.address, toWei('0.01'), toWei('0.1'))
debug('deploying NFT test contract...')
const NFTFactory = await hre.ethers.getContractFactory("NFT", wallet, overrides)
NFT = await NFTFactory.deploy()
await NFT.initialize('NFT Token', 'NFT')
})

it('should receive an ERC721 token when using safeTransferFrom', async () => {
expect(await NFT.balanceOf(prizePool.address)).to.equal('0')
expect(await NFT.simulateSafeTransferFrom(wallet.address, prizePool.address, 0))
.to.emit(NFT, 'Transfer')
.withArgs(wallet.address, prizePool.address, 0);
expect(await NFT.balanceOf(prizePool.address)).to.equal('1')
})

})
});