feat(CommunityVault): add erc721 deposit function
This commit is contained in:
parent
77eaec3eed
commit
591cb554b1
|
@ -4,9 +4,12 @@ AddEntryTest:test_RevertWhen_InvalidAddress() (gas: 25133)
|
||||||
AddEntryTest:test_RevertWhen_SenderIsNotTokenDeployer() (gas: 14827)
|
AddEntryTest:test_RevertWhen_SenderIsNotTokenDeployer() (gas: 14827)
|
||||||
CommunityERC20Test:test_Deployment() (gas: 35198)
|
CommunityERC20Test:test_Deployment() (gas: 35198)
|
||||||
CommunityTokenDeployerTest:test_Deployment() (gas: 14805)
|
CommunityTokenDeployerTest:test_Deployment() (gas: 14805)
|
||||||
CommunityVaultBaseERC20Test:test_Deployment() (gas: 10436)
|
CommunityVaultBaseERC20Test:test_Deployment() (gas: 10572)
|
||||||
CommunityVaultBaseERC721Test:test_Deployment() (gas: 10436)
|
CommunityVaultBaseERC721Test:test_Deployment() (gas: 10572)
|
||||||
CommunityVaultTest:test_Deployment() (gas: 10436)
|
CommunityVaultBaseTransferERC721Test:test_Deployment() (gas: 10572)
|
||||||
|
CommunityVaultDepositERC721Test:testSuccessfulDepositERC721() (gas: 184700)
|
||||||
|
CommunityVaultDepositERC721Test:test_Deployment() (gas: 10714)
|
||||||
|
CommunityVaultTest:test_Deployment() (gas: 10572)
|
||||||
CreateTest:test_Create() (gas: 2374801)
|
CreateTest:test_Create() (gas: 2374801)
|
||||||
CreateTest:test_Create() (gas: 2661968)
|
CreateTest:test_Create() (gas: 2661968)
|
||||||
CreateTest:test_RevertWhen_InvalidOwnerTokenAddress() (gas: 15523)
|
CreateTest:test_RevertWhen_InvalidOwnerTokenAddress() (gas: 15523)
|
||||||
|
@ -30,6 +33,9 @@ DeploymentTest:test_Deployment() (gas: 14671)
|
||||||
DeploymentTest:test_Deployment() (gas: 14671)
|
DeploymentTest:test_Deployment() (gas: 14671)
|
||||||
DeploymentTest:test_Deployment() (gas: 17295)
|
DeploymentTest:test_Deployment() (gas: 17295)
|
||||||
DeploymentTest:test_Deployment() (gas: 36430)
|
DeploymentTest:test_Deployment() (gas: 36430)
|
||||||
|
DepositERC20Test:testDepositZeroTokens() (gas: 15199)
|
||||||
|
DepositERC20Test:testSuccessfulDepositERC20() (gas: 85584)
|
||||||
|
DepositERC20Test:test_Deployment() (gas: 10594)
|
||||||
GetEntryTest:test_ReturnZeroAddressIfEntryDoesNotExist() (gas: 11906)
|
GetEntryTest:test_ReturnZeroAddressIfEntryDoesNotExist() (gas: 11906)
|
||||||
MintToTest:test_Deployment() (gas: 35220)
|
MintToTest:test_Deployment() (gas: 35220)
|
||||||
MintToTest:test_Deployment() (gas: 83308)
|
MintToTest:test_Deployment() (gas: 83308)
|
||||||
|
@ -82,16 +88,18 @@ SetTokenDeployerAddressTest:test_RevertWhen_SenderIsNotOwner() (gas: 12438)
|
||||||
SetTokenDeployerAddressTest:test_RevertWhen_SenderIsNotOwner() (gas: 12438)
|
SetTokenDeployerAddressTest:test_RevertWhen_SenderIsNotOwner() (gas: 12438)
|
||||||
SetTokenDeployerAddressTest:test_SetTokenDeployerAddress() (gas: 22768)
|
SetTokenDeployerAddressTest:test_SetTokenDeployerAddress() (gas: 22768)
|
||||||
SetTokenDeployerAddressTest:test_SetTokenDeployerAddress() (gas: 22768)
|
SetTokenDeployerAddressTest:test_SetTokenDeployerAddress() (gas: 22768)
|
||||||
TransferERC20ByAdminTest:test_AdminCanTransferERC20() (gas: 84406)
|
TransferERC20ByAdminTest:test_AdminCanTransferERC20() (gas: 97818)
|
||||||
TransferERC20ByAdminTest:test_Deployment() (gas: 10556)
|
TransferERC20ByAdminTest:test_Deployment() (gas: 10714)
|
||||||
TransferERC20ByAdminTest:test_LengthMismatch() (gas: 31872)
|
TransferERC20ByAdminTest:test_LengthMismatch() (gas: 26146)
|
||||||
TransferERC20ByAdminTest:test_NoRecipients() (gas: 25198)
|
TransferERC20ByAdminTest:test_NoRecipients() (gas: 19516)
|
||||||
TransferERC20ByAdminTest:test_TransferAmountZero() (gas: 61686)
|
TransferERC20ByAdminTest:test_TransferAmountZero() (gas: 66057)
|
||||||
TransferERC20ByNonAdminTest:test_Deployment() (gas: 10458)
|
TransferERC20ByAdminTest:test_TransferERC20AmountTooBig() (gas: 59079)
|
||||||
TransferERC20ByNonAdminTest:test_revertIfCalledByNonAdmin() (gas: 35570)
|
TransferERC20ByNonAdminTest:test_Deployment() (gas: 10594)
|
||||||
TransferERC721ByAdminTest:test_AdminCanTransferERC721() (gas: 107114)
|
TransferERC20ByNonAdminTest:test_revertIfCalledByNonAdmin() (gas: 29912)
|
||||||
TransferERC721ByAdminTest:test_Deployment() (gas: 10556)
|
TransferERC721ByAdminTest:test_AdminCanTransferERC721() (gas: 141776)
|
||||||
TransferERC721ByAdminTest:test_LengthMismatch() (gas: 31875)
|
TransferERC721ByAdminTest:test_Deployment() (gas: 10670)
|
||||||
TransferERC721ByAdminTest:test_NoRecipients() (gas: 25213)
|
TransferERC721ByAdminTest:test_LengthMismatch() (gas: 26156)
|
||||||
TransferERC721ByNonAdminTest:test_Deployment() (gas: 10458)
|
TransferERC721ByAdminTest:test_NoRecipients() (gas: 19506)
|
||||||
TransferERC721ByNonAdminTest:test_RevertIfCalledByNonAdmin() (gas: 35563)
|
TransferERC721ByAdminTest:test_RevertOnTransferERC721IfNotDeposited() (gas: 32736)
|
||||||
|
TransferERC721ByNonAdminTest:test_Deployment() (gas: 10714)
|
||||||
|
TransferERC721ByNonAdminTest:test_RevertIfCalledByNonAdmin() (gas: 29953)
|
||||||
|
|
|
@ -5,6 +5,8 @@ pragma solidity ^0.8.17;
|
||||||
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||||
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
|
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
|
||||||
|
import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
|
||||||
|
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
|
||||||
import { CommunityOwnable } from "./CommunityOwnable.sol";
|
import { CommunityOwnable } from "./CommunityOwnable.sol";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,8 +16,9 @@ import { CommunityOwnable } from "./CommunityOwnable.sol";
|
||||||
* Only community owners, as defined in the CommunityOwnable contract, have
|
* Only community owners, as defined in the CommunityOwnable contract, have
|
||||||
* permissions to transfer these tokens out of the vault.
|
* permissions to transfer these tokens out of the vault.
|
||||||
*/
|
*/
|
||||||
contract CommunityVault is CommunityOwnable {
|
contract CommunityVault is CommunityOwnable, IERC721Receiver {
|
||||||
using SafeERC20 for IERC20;
|
using SafeERC20 for IERC20;
|
||||||
|
using EnumerableSet for EnumerableSet.UintSet;
|
||||||
|
|
||||||
event ERC20Deposited(address indexed depositor, address indexed token, uint256 amount);
|
event ERC20Deposited(address indexed depositor, address indexed token, uint256 amount);
|
||||||
event ERC721Deposited(address indexed depositor, address indexed token, uint256 tokenId);
|
event ERC721Deposited(address indexed depositor, address indexed token, uint256 tokenId);
|
||||||
|
@ -25,8 +28,12 @@ contract CommunityVault is CommunityOwnable {
|
||||||
error CommunityVault_TransferAmountZero();
|
error CommunityVault_TransferAmountZero();
|
||||||
error CommunityVault_ERC20TransferAmountTooBig();
|
error CommunityVault_ERC20TransferAmountTooBig();
|
||||||
error CommunityVault_DepositAmountZero();
|
error CommunityVault_DepositAmountZero();
|
||||||
|
error CommunityVault_IndexOutOfBounds();
|
||||||
|
error CommunityVault_ERC721TokenAlreadyDeposited();
|
||||||
|
error CommunityVault_ERC721TokenNotDeposited();
|
||||||
|
|
||||||
mapping(address => uint256) public erc20TokenBalances;
|
mapping(address => uint256) public erc20TokenBalances;
|
||||||
|
mapping(address => EnumerableSet.UintSet) private erc721TokenIds;
|
||||||
|
|
||||||
constructor(address _ownerToken, address _masterToken) CommunityOwnable(_ownerToken, _masterToken) { }
|
constructor(address _ownerToken, address _masterToken) CommunityOwnable(_ownerToken, _masterToken) { }
|
||||||
|
|
||||||
|
@ -50,6 +57,50 @@ contract CommunityVault is CommunityOwnable {
|
||||||
emit ERC20Deposited(msg.sender, token, amount);
|
emit ERC20Deposited(msg.sender, token, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Allows anyone to deposit multiple ERC721 tokens into the vault.
|
||||||
|
* @param token The address of the ERC721 token to deposit.
|
||||||
|
* @param tokenIds The IDs of the tokens to deposit.
|
||||||
|
*/
|
||||||
|
function depositERC721(address token, uint256[] memory tokenIds) public {
|
||||||
|
for (uint256 i = 0; i < tokenIds.length; i++) {
|
||||||
|
// Add the token ID to the EnumerableSet for the given token
|
||||||
|
bool added = erc721TokenIds[token].add(tokenIds[i]);
|
||||||
|
if (!added) {
|
||||||
|
revert CommunityVault_ERC721TokenAlreadyDeposited();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer the token from the sender to this contract
|
||||||
|
IERC721(token).safeTransferFrom(msg.sender, address(this), tokenIds[i]);
|
||||||
|
|
||||||
|
// Emit an event for the deposit
|
||||||
|
emit ERC721Deposited(msg.sender, token, tokenIds[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Gets the count of ERC721 tokens deposited for a given token address.
|
||||||
|
* @param token The address of the ERC721 token.
|
||||||
|
* @return The count of tokens deposited.
|
||||||
|
*/
|
||||||
|
function erc721TokenBalances(address token) public view returns (uint256) {
|
||||||
|
return erc721TokenIds[token].length();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Retrieves a deposited ERC721 token ID by index.
|
||||||
|
* @param token The address of the ERC721 token.
|
||||||
|
* @param index The index of the token ID to retrieve.
|
||||||
|
* @return The token ID at the given index.
|
||||||
|
*/
|
||||||
|
function getERC721DepositedTokenByIndex(address token, uint256 index) public view returns (uint256) {
|
||||||
|
if (index >= erc721TokenIds[token].length()) {
|
||||||
|
revert CommunityVault_IndexOutOfBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
return erc721TokenIds[token].at(index);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Transfers ERC20 tokens to a list of addresses.
|
* @dev Transfers ERC20 tokens to a list of addresses.
|
||||||
* @param token The ERC20 token address.
|
* @param token The ERC20 token address.
|
||||||
|
@ -109,7 +160,21 @@ contract CommunityVault is CommunityOwnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint256 i = 0; i < recipients.length; i++) {
|
for (uint256 i = 0; i < recipients.length; i++) {
|
||||||
|
bool removed = erc721TokenIds[token].remove(tokenIds[i]);
|
||||||
|
if (!removed) {
|
||||||
|
revert CommunityVault_ERC721TokenNotDeposited();
|
||||||
|
}
|
||||||
|
|
||||||
IERC721(token).safeTransferFrom(address(this), recipients[i], tokenIds[i]);
|
IERC721(token).safeTransferFrom(address(this), recipients[i], tokenIds[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Handles the receipt of an ERC721 token.
|
||||||
|
* @return bytes4 Returns `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
|
||||||
|
* to indicate the contract implements `onERC721Received` as per ERC721.
|
||||||
|
*/
|
||||||
|
function onERC721Received(address, address, uint256, bytes calldata) public pure override returns (bytes4) {
|
||||||
|
return this.onERC721Received.selector;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,26 +168,42 @@ contract CommunityVaultBaseERC721Test is CommunityVaultTest {
|
||||||
function setUp() public virtual override {
|
function setUp() public virtual override {
|
||||||
CommunityVaultTest.setUp();
|
CommunityVaultTest.setUp();
|
||||||
|
|
||||||
// mint 2 token to user
|
// mint 4 token to user
|
||||||
address user = accounts[0];
|
address user = accounts[0];
|
||||||
erc721Token.mint(user);
|
erc721Token.mint(user);
|
||||||
erc721Token.mint(user);
|
erc721Token.mint(user);
|
||||||
|
erc721Token.mint(user);
|
||||||
|
erc721Token.mint(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract CommunityVaultBaseTransferERC721Test is CommunityVaultBaseERC721Test {
|
||||||
|
function setUp() public virtual override {
|
||||||
|
CommunityVaultBaseERC721Test.setUp();
|
||||||
|
|
||||||
|
address user = accounts[0];
|
||||||
|
|
||||||
// user transfer 2 tokens to the vault
|
// user transfer 2 tokens to the vault
|
||||||
|
uint256[] memory ids = new uint256[](3);
|
||||||
|
ids[0] = 0;
|
||||||
|
ids[1] = 1;
|
||||||
|
ids[2] = 2;
|
||||||
|
|
||||||
vm.startPrank(user);
|
vm.startPrank(user);
|
||||||
erc721Token.transferFrom(user, address(vault), 0);
|
erc721Token.approve(address(vault), ids[0]);
|
||||||
erc721Token.transferFrom(user, address(vault), 1);
|
erc721Token.approve(address(vault), ids[1]);
|
||||||
|
erc721Token.approve(address(vault), ids[2]);
|
||||||
|
vault.depositERC721(address(erc721Token), ids);
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contract TransferERC721ByNonAdminTest is CommunityVaultBaseERC721Test {
|
contract TransferERC721ByNonAdminTest is CommunityVaultBaseTransferERC721Test {
|
||||||
function setUp() public virtual override {
|
function setUp() public virtual override {
|
||||||
CommunityVaultBaseERC721Test.setUp();
|
CommunityVaultBaseTransferERC721Test.setUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_RevertIfCalledByNonAdmin() public {
|
function test_RevertIfCalledByNonAdmin() public {
|
||||||
assertEq(erc721Token.balanceOf(address(vault)), 2);
|
|
||||||
uint256[] memory ids = new uint256[](2);
|
uint256[] memory ids = new uint256[](2);
|
||||||
ids[0] = 0;
|
ids[0] = 0;
|
||||||
ids[1] = 1;
|
ids[1] = 1;
|
||||||
|
@ -199,14 +215,12 @@ contract TransferERC721ByNonAdminTest is CommunityVaultBaseERC721Test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contract TransferERC721ByAdminTest is CommunityVaultBaseERC721Test {
|
contract TransferERC721ByAdminTest is CommunityVaultBaseTransferERC721Test {
|
||||||
function setUp() public virtual override {
|
function setUp() public virtual override {
|
||||||
CommunityVaultBaseERC721Test.setUp();
|
CommunityVaultBaseTransferERC721Test.setUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_LengthMismatch() public {
|
function test_LengthMismatch() public {
|
||||||
assertEq(erc721Token.balanceOf(address(vault)), 2);
|
|
||||||
|
|
||||||
uint256[] memory ids = new uint256[](1);
|
uint256[] memory ids = new uint256[](1);
|
||||||
ids[0] = 0;
|
ids[0] = 0;
|
||||||
|
|
||||||
|
@ -216,8 +230,6 @@ contract TransferERC721ByAdminTest is CommunityVaultBaseERC721Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_NoRecipients() public {
|
function test_NoRecipients() public {
|
||||||
assertEq(erc721Token.balanceOf(address(vault)), 2);
|
|
||||||
|
|
||||||
uint256[] memory ids = new uint256[](0);
|
uint256[] memory ids = new uint256[](0);
|
||||||
address[] memory tmpAccounts = new address[](0);
|
address[] memory tmpAccounts = new address[](0);
|
||||||
|
|
||||||
|
@ -227,10 +239,16 @@ contract TransferERC721ByAdminTest is CommunityVaultBaseERC721Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_AdminCanTransferERC721() public {
|
function test_AdminCanTransferERC721() public {
|
||||||
assertEq(erc721Token.balanceOf(address(vault)), 2);
|
assertEq(erc721Token.balanceOf(address(vault)), 3);
|
||||||
|
assertEq(vault.erc721TokenBalances(address(erc721Token)), 3);
|
||||||
|
|
||||||
|
// accounts[0] has 1 token with id 3
|
||||||
|
assertEq(erc721Token.balanceOf(accounts[0]), 1);
|
||||||
|
assertEq(erc721Token.balanceOf(accounts[1]), 0);
|
||||||
|
|
||||||
assertEq(erc721Token.ownerOf(0), address(vault));
|
assertEq(erc721Token.ownerOf(0), address(vault));
|
||||||
assertEq(erc721Token.ownerOf(1), address(vault));
|
assertEq(erc721Token.ownerOf(1), address(vault));
|
||||||
|
assertEq(erc721Token.ownerOf(2), address(vault));
|
||||||
|
|
||||||
uint256[] memory ids = new uint256[](2);
|
uint256[] memory ids = new uint256[](2);
|
||||||
ids[0] = 0;
|
ids[0] = 0;
|
||||||
|
@ -239,6 +257,49 @@ contract TransferERC721ByAdminTest is CommunityVaultBaseERC721Test {
|
||||||
vm.prank(deployer);
|
vm.prank(deployer);
|
||||||
vault.transferERC721(address(erc721Token), accounts, ids);
|
vault.transferERC721(address(erc721Token), accounts, ids);
|
||||||
|
|
||||||
assertEq(erc721Token.balanceOf(address(vault)), 0);
|
assertEq(erc721Token.balanceOf(address(vault)), 1);
|
||||||
|
assertEq(vault.erc721TokenBalances(address(erc721Token)), 1);
|
||||||
|
|
||||||
|
assertEq(erc721Token.balanceOf(accounts[0]), 2);
|
||||||
|
assertEq(erc721Token.balanceOf(accounts[1]), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_RevertOnTransferERC721IfNotDeposited() public {
|
||||||
|
// id 3 is not deposited
|
||||||
|
assertEq(erc721Token.ownerOf(3), address(accounts[0]));
|
||||||
|
|
||||||
|
uint256[] memory ids = new uint256[](1);
|
||||||
|
ids[0] = 3;
|
||||||
|
|
||||||
|
address[] memory accountsList = new address[](1);
|
||||||
|
accountsList[0] = accounts[0];
|
||||||
|
|
||||||
|
vm.prank(deployer);
|
||||||
|
vm.expectRevert(CommunityVault.CommunityVault_ERC721TokenNotDeposited.selector);
|
||||||
|
vault.transferERC721(address(erc721Token), accountsList, ids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract CommunityVaultDepositERC721Test is CommunityVaultBaseERC721Test {
|
||||||
|
function setUp() public virtual override {
|
||||||
|
CommunityVaultBaseERC721Test.setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testSuccessfulDepositERC721() public {
|
||||||
|
uint256[] memory ids = new uint256[](2);
|
||||||
|
ids[0] = 0;
|
||||||
|
ids[1] = 1;
|
||||||
|
|
||||||
|
uint256 initialVaultBalance = erc721Token.balanceOf(address(vault));
|
||||||
|
uint256 initialTokenBalanceValue = vault.erc721TokenBalances(address(erc721Token));
|
||||||
|
|
||||||
|
vm.startPrank(accounts[0]);
|
||||||
|
erc721Token.approve(address(vault), ids[0]);
|
||||||
|
erc721Token.approve(address(vault), ids[1]);
|
||||||
|
vault.depositERC721(address(erc721Token), ids);
|
||||||
|
vm.stopPrank();
|
||||||
|
|
||||||
|
assertEq(erc721Token.balanceOf(address(vault)), initialVaultBalance + 2);
|
||||||
|
assertEq(vault.erc721TokenBalances(address(erc721Token)), initialTokenBalanceValue + 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue