diff --git a/contracts/Staking.sol b/contracts/Staking.sol index 9b13fa5..3edf53e 100644 --- a/contracts/Staking.sol +++ b/contracts/Staking.sol @@ -2,13 +2,12 @@ pragma solidity ^0.8.4; import "@openzeppelin/contracts/interfaces/IERC721.sol"; - -import "./Camp.sol"; +import "./interfaces/ICamp.sol"; /* solhint-disable not-rely-on-time */ contract Staking { - Camp public camp; - IERC721 public dappCampWarriors; + address public campAddress; + address public dappCampWarriorsAddress; struct Stake { address owner; @@ -27,8 +26,8 @@ contract Staking { uint256 public rewardPerSecondInWei = 1000000000000000000; constructor(address _camp, address _dappCampWarriors) { - camp = Camp(_camp); - dappCampWarriors = IERC721(_dappCampWarriors); + campAddress = _camp; + dappCampWarriorsAddress = _dappCampWarriors; } function stake(uint256 tokenId) public { @@ -39,11 +38,11 @@ contract Staking { * - Staking NFTs that are already staked (since the owner is this contract) */ require( - dappCampWarriors.ownerOf(tokenId) == msg.sender, + IERC721(dappCampWarriorsAddress).ownerOf(tokenId) == msg.sender, "Staking: only the owner can stake an NFT" ); - dappCampWarriors.transferFrom(msg.sender, address(this), tokenId); + IERC721(dappCampWarriorsAddress).transferFrom(msg.sender, address(this), tokenId); // solhint-disable-next-line not-rely-on-time staked[tokenId] = Stake(msg.sender, tokenId, block.timestamp); @@ -54,24 +53,25 @@ contract Staking { * @dev This prevents 3 things: * - Unstaking non-existing NFTs (ERC721 ownerOf validates existence) * - Unstaking if msg.sender is not the owner - * - Unstaking NFTs that are not staked (since staked[tokenId].owner will be the zero address) + * - Unstaking NFTs that are not staked (since staking.owner will be the zero address) */ + Stake memory staking = staked[tokenId]; + require( - staked[tokenId].owner == msg.sender, + staking.owner == msg.sender, "Staking: only the owner can unstake an NFT" ); require( - staked[tokenId].initTimestamp != block.timestamp, + staking.initTimestamp != block.timestamp, "Staking: cannot unstake an NFT in the same block it was staked" ); - uint256 stakedSeconds = block.timestamp - staked[tokenId].initTimestamp; - - delete staked[tokenId]; + uint256 stakedSeconds = block.timestamp - staking.initTimestamp; - dappCampWarriors.transferFrom(address(this), msg.sender, tokenId); + delete staking; - camp.mint(msg.sender, stakedSeconds * rewardPerSecondInWei); + IERC721(dappCampWarriorsAddress).transferFrom(address(this), msg.sender, tokenId); + ICamp(campAddress).mint(msg.sender, stakedSeconds * rewardPerSecondInWei); } } /* solhint-enable not-rely-on-time */ diff --git a/contracts/interfaces/ICamp.sol b/contracts/interfaces/ICamp.sol new file mode 100644 index 0000000..816a0fd --- /dev/null +++ b/contracts/interfaces/ICamp.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +interface ICamp { + function mint(address account, uint256 amount) external; +} diff --git a/test/Staking.spec.ts b/test/Staking.spec.ts index c2408ca..5764791 100644 --- a/test/Staking.spec.ts +++ b/test/Staking.spec.ts @@ -154,16 +154,39 @@ describe("Camp tests", () => { ethers.utils.parseEther("1") ); }); + }); - it("Should remove the NFT from the `staked` mapping", async () => { - await createValidStake(); - const stakedData1 = await stakingContract.staked(1); - expect(stakedData1.tokenId).to.equal(1); + describe("gasCheck", () => { + it("Should check the gas used for contract creation is below minimum threshold", async () => { + const GAS_THRESHOLD = 755_000; - await stakingContract.unstake(1); + const StakingFactory = await ethers.getContractFactory("Staking"); + const stakingContract = await StakingFactory.deploy(campContract.address, dappCampWarriorsContract.address); + + const gasLimit = stakingContract.deployTransaction.gasLimit; + + expect(gasLimit).lt(GAS_THRESHOLD); + }); + + it("Should check the gas used for staking is below minimum threshold", async () => { + const GAS_THRESHOLD = 142_000; + + await ( + await dappCampWarriorsContract.approve(stakingContract.address, 1) + ).wait(); + + const gasUsed = await stakingContract.estimateGas.stake(1); + + expect(gasUsed).lt(GAS_THRESHOLD); + }); + + it("Should check the gas used for unstake is below minimum threshold", async () => { + const GAS_THRESHOLD = 120_000; + + await createValidStake(); + const gasUsed = await stakingContract.estimateGas.unstake(1); - const stakedData2 = await stakingContract.staked(1); - expect(stakedData2.tokenId).to.equal(0); + expect(gasUsed).lt(GAS_THRESHOLD); }); }); });