From 69c0529f60cc7b892e825c1fdd2a3c88d7544d0c Mon Sep 17 00:00:00 2001 From: Rafal Czajkowski Date: Mon, 30 Oct 2023 13:03:16 +0100 Subject: [PATCH 1/8] Create TokenStaking contract A token staking contract for a specified standard ERC20 token. A holder of the specified token can stake its tokens to this contract and recover the stake after undelegation period is over. --- core/contracts/staking/TokenStaking.sol | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 core/contracts/staking/TokenStaking.sol diff --git a/core/contracts/staking/TokenStaking.sol b/core/contracts/staking/TokenStaking.sol new file mode 100644 index 000000000..defd83e1a --- /dev/null +++ b/core/contracts/staking/TokenStaking.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-3.0-only + +pragma solidity ^0.8.9; + +/// @title TokenStaking +/// @notice A token staking contract for a specified standard ERC20 token. A +/// holder of the specified token can stake its tokens to this contract +/// and recover the stake after undelegation period is over. +contract TokenStaking { + // TODO: use IERC20 contract as type + address internal immutable token; + + constructor(address _token) { + require( + address(_token) != address(0), + "Token can not be the zero address" + ); + + token = _token; + } +} From 8e9e5135a1db3328609a24eb10618470bf9e1d05 Mon Sep 17 00:00:00 2001 From: Rafal Czajkowski Date: Mon, 30 Oct 2023 13:03:32 +0100 Subject: [PATCH 2/8] Add `@openzeppelin/contracts` dependency Use ERC20 contracts and libraries to interact with staking token in staking contract. --- core/contracts/staking/TokenStaking.sol | 12 ++++++++---- core/package.json | 3 +++ core/yarn.lock | 5 +++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/core/contracts/staking/TokenStaking.sol b/core/contracts/staking/TokenStaking.sol index defd83e1a..aa61fc59d 100644 --- a/core/contracts/staking/TokenStaking.sol +++ b/core/contracts/staking/TokenStaking.sol @@ -1,16 +1,20 @@ // SPDX-License-Identifier: GPL-3.0-only -pragma solidity ^0.8.9; +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; /// @title TokenStaking /// @notice A token staking contract for a specified standard ERC20 token. A /// holder of the specified token can stake its tokens to this contract /// and recover the stake after undelegation period is over. contract TokenStaking { - // TODO: use IERC20 contract as type - address internal immutable token; + using SafeERC20 for IERC20; + + IERC20 internal immutable token; - constructor(address _token) { + constructor(IERC20 _token) { require( address(_token) != address(0), "Token can not be the zero address" diff --git a/core/package.json b/core/package.json index 2b5649c0b..6b08387c1 100644 --- a/core/package.json +++ b/core/package.json @@ -56,5 +56,8 @@ "ts-node": ">=8.0.0", "typechain": "^8.1.0", "typescript": ">=4.5.0" + }, + "dependencies": { + "@openzeppelin/contracts": "^5.0.0" } } diff --git a/core/yarn.lock b/core/yarn.lock index e2d8e3206..f61f4d6f0 100644 --- a/core/yarn.lock +++ b/core/yarn.lock @@ -879,6 +879,11 @@ resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-waffle/-/hardhat-waffle-2.0.6.tgz#d11cb063a5f61a77806053e54009c40ddee49a54" integrity sha512-+Wz0hwmJGSI17B+BhU/qFRZ1l6/xMW82QGXE/Gi+WTmwgJrQefuBs1lIf7hzQ1hLk6hpkvb/zwcNkpVKRYTQYg== +"@openzeppelin/contracts@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.0.0.tgz#ee0e4b4564f101a5c4ee398cd4d73c0bd92b289c" + integrity sha512-bv2sdS6LKqVVMLI5+zqnNrNU/CA+6z6CmwFXm/MzmOPBRSO5reEJN7z0Gbzvs0/bv/MZZXNklubpwy3v2+azsw== + "@openzeppelin/defender-admin-client@^1.48.0": version "1.49.0" resolved "https://registry.yarnpkg.com/@openzeppelin/defender-admin-client/-/defender-admin-client-1.49.0.tgz#ed07318ccba10ac8a8a33cf594fc18b7ab5889f9" From 2e93f32b4ff1467e2eb9f5b27417b0e3588e0d1a Mon Sep 17 00:00:00 2001 From: Rafal Czajkowski Date: Mon, 30 Oct 2023 13:03:36 +0100 Subject: [PATCH 3/8] Add `stake` function to `TokenStaking` contract Add function that stakes owner's tokens in the staking contract - tracks user's staked funds and emit `Staked` event. The minimum amount is required. --- core/contracts/staking/TokenStaking.sol | 17 ++++++- core/contracts/test/TestToken.sol | 13 +++++ core/test/staking/TokenStaking.test.ts | 67 +++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 core/contracts/test/TestToken.sol create mode 100644 core/test/staking/TokenStaking.test.ts diff --git a/core/contracts/staking/TokenStaking.sol b/core/contracts/staking/TokenStaking.sol index aa61fc59d..d1b9d2ccd 100644 --- a/core/contracts/staking/TokenStaking.sol +++ b/core/contracts/staking/TokenStaking.sol @@ -11,9 +11,13 @@ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; /// and recover the stake after undelegation period is over. contract TokenStaking { using SafeERC20 for IERC20; - + + event Staked(address indexed account, uint256 amount); + IERC20 internal immutable token; + mapping(address => uint256) public balanceOf; + constructor(IERC20 _token) { require( address(_token) != address(0), @@ -22,4 +26,15 @@ contract TokenStaking { token = _token; } + + /// @notice Stakes the owner's tokens in the staking contract. + /// @param amount Approved amount for the transfer and stake. + function stake(uint256 amount) external { + require(amount > 0, "Amount is less than minimum"); + + balanceOf[msg.sender] += amount; + + emit Staked(msg.sender, amount); + token.safeTransferFrom(msg.sender, address(this), amount); + } } diff --git a/core/contracts/test/TestToken.sol b/core/contracts/test/TestToken.sol new file mode 100644 index 000000000..749165b4c --- /dev/null +++ b/core/contracts/test/TestToken.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +pragma solidity 0.8.20; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract Token is ERC20 { + constructor() ERC20("Test Token", "TEST") {} + + function mint(address account, uint256 value) external { + _mint(account, value); + } +} diff --git a/core/test/staking/TokenStaking.test.ts b/core/test/staking/TokenStaking.test.ts new file mode 100644 index 000000000..fb2e8655a --- /dev/null +++ b/core/test/staking/TokenStaking.test.ts @@ -0,0 +1,67 @@ +import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers" +import { ethers } from "hardhat" +import { expect } from "chai" +import { Token, TokenStaking } from "../../typechain" +import { WeiPerEther } from "ethers" +import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers" +import { before } from "mocha" + +async function tokenStakingFixture() { + const [deployer, tokenHolder] = await ethers.getSigners() + const StakingToken = await ethers.getContractFactory("Token") + const token = await StakingToken.deploy() + + const amountToMint = WeiPerEther * 10000n + + token.mint(tokenHolder, amountToMint) + + const TokenStaking = await ethers.getContractFactory("TokenStaking") + const tokenStaking = await TokenStaking.deploy(await token.getAddress()) + + return { tokenStaking, token, tokenHolder } +} + +describe("TokenStaking", () => { + let tokenStaking: TokenStaking + let token: Token + let tokenHolder: HardhatEthersSigner + + beforeEach(async () => { + const { + tokenStaking: _tokenStaking, + token: _token, + tokenHolder: _tokenHolder, + } = await loadFixture(tokenStakingFixture) + + tokenStaking = _tokenStaking + token = _token + tokenHolder = _tokenHolder + }) + + describe("staking", () => { + beforeEach(async () => { + // Infinite approval for staking contract. + await token + .connect(tokenHolder) + .approve(await tokenStaking.getAddress(), ethers.MaxUint256) + }) + + it("should stake tokens", async () => { + const tokenHolderAddress = await tokenHolder.getAddress() + const tokenBalance = await token.balanceOf(tokenHolderAddress) + + await expect(tokenStaking.connect(tokenHolder).stake(tokenBalance)) + .to.emit(tokenStaking, "Staked") + .withArgs(tokenHolderAddress, tokenBalance) + expect(await tokenStaking.balanceOf(tokenHolderAddress)).to.be.eq( + tokenBalance, + ) + expect(await token.balanceOf(tokenHolderAddress)).to.be.eq(0) + }) + + it("should revert if the staked amount is less than required minimum", async () => { + await expect(tokenStaking.connect(tokenHolder).stake(0)) + .to.be.revertedWith("Amount is less than minimum") + }) + }) +}) From 209d0cc13957201a2ce8e6c1c78660d313dc1ddc Mon Sep 17 00:00:00 2001 From: Rafal Czajkowski Date: Mon, 30 Oct 2023 13:03:39 +0100 Subject: [PATCH 4/8] Update `TokenStaking` contract Add support for `approveAndCall`/`receiveApproval` pattern. The tBTC token contract that will be a staking token supports this pattern. To be able to stake in one transaction (instead of 2: approve + stake) we must implement the `RecieveApproval` interface. The token staking contract receives approval to spend tokens and create a stake for a given account. --- core/contracts/shared/IReceiveApproval.sol | 16 ++++++ core/contracts/staking/TokenStaking.sol | 31 +++++++++-- core/contracts/test/TestToken.sol | 19 +++++++ core/test/staking/TokenStaking.test.ts | 62 +++++++++++++++------- 4 files changed, 104 insertions(+), 24 deletions(-) create mode 100644 core/contracts/shared/IReceiveApproval.sol diff --git a/core/contracts/shared/IReceiveApproval.sol b/core/contracts/shared/IReceiveApproval.sol new file mode 100644 index 000000000..175e32eb8 --- /dev/null +++ b/core/contracts/shared/IReceiveApproval.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-3.0-only + +pragma solidity ^0.8.20; + +/// @notice An interface that should be implemented by contracts supporting +/// `approveAndCall`/`receiveApproval` pattern. +interface IReceiveApproval { + /// @notice Receives approval to spend tokens. Called as a result of + /// `approveAndCall` call on the token. + function receiveApproval( + address from, + uint256 amount, + address token, + bytes calldata extraData + ) external; +} diff --git a/core/contracts/staking/TokenStaking.sol b/core/contracts/staking/TokenStaking.sol index d1b9d2ccd..a9051040d 100644 --- a/core/contracts/staking/TokenStaking.sol +++ b/core/contracts/staking/TokenStaking.sol @@ -4,12 +4,13 @@ pragma solidity ^0.8.20; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "../shared/IReceiveApproval.sol"; /// @title TokenStaking /// @notice A token staking contract for a specified standard ERC20 token. A /// holder of the specified token can stake its tokens to this contract /// and recover the stake after undelegation period is over. -contract TokenStaking { +contract TokenStaking is IReceiveApproval { using SafeERC20 for IERC20; event Staked(address indexed account, uint256 amount); @@ -27,14 +28,36 @@ contract TokenStaking { token = _token; } + /// @notice Receives approval of token transfer and stakes the approved + /// amount or adds the approved amount to an existing stake. + /// @dev Requires that the provided token contract be the same one linked to + /// this contract. + /// @param from The owner of the tokens who approved them to transfer. + /// @param amount Approved amount for the transfer and stake. + /// @param _token Token contract address. + function receiveApproval( + address from, + uint256 amount, + address _token, + bytes calldata + ) external override { + require(_token == address(token), "Unrecognized token"); + _stake(from, amount); + } + /// @notice Stakes the owner's tokens in the staking contract. /// @param amount Approved amount for the transfer and stake. function stake(uint256 amount) external { + _stake(msg.sender, amount); + } + + function _stake(address account, uint256 amount) private { require(amount > 0, "Amount is less than minimum"); + require(account != address(0), "Can not be the zero address"); - balanceOf[msg.sender] += amount; + balanceOf[account] += amount; - emit Staked(msg.sender, amount); - token.safeTransferFrom(msg.sender, address(this), amount); + emit Staked(account, amount); + token.safeTransferFrom(account, address(this), amount); } } diff --git a/core/contracts/test/TestToken.sol b/core/contracts/test/TestToken.sol index 749165b4c..c061cf278 100644 --- a/core/contracts/test/TestToken.sol +++ b/core/contracts/test/TestToken.sol @@ -3,6 +3,8 @@ pragma solidity 0.8.20; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "../shared/IReceiveApproval.sol"; + contract Token is ERC20 { constructor() ERC20("Test Token", "TEST") {} @@ -10,4 +12,21 @@ contract Token is ERC20 { function mint(address account, uint256 value) external { _mint(account, value); } + + function approveAndCall( + address spender, + uint256 amount, + bytes memory extraData + ) external returns (bool) { + if (approve(spender, amount)) { + IReceiveApproval(spender).receiveApproval( + msg.sender, + amount, + address(this), + extraData + ); + return true; + } + return false; + } } diff --git a/core/test/staking/TokenStaking.test.ts b/core/test/staking/TokenStaking.test.ts index fb2e8655a..076313950 100644 --- a/core/test/staking/TokenStaking.test.ts +++ b/core/test/staking/TokenStaking.test.ts @@ -4,7 +4,6 @@ import { expect } from "chai" import { Token, TokenStaking } from "../../typechain" import { WeiPerEther } from "ethers" import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers" -import { before } from "mocha" async function tokenStakingFixture() { const [deployer, tokenHolder] = await ethers.getSigners() @@ -39,29 +38,52 @@ describe("TokenStaking", () => { }) describe("staking", () => { - beforeEach(async () => { - // Infinite approval for staking contract. - await token - .connect(tokenHolder) - .approve(await tokenStaking.getAddress(), ethers.MaxUint256) - }) + describe("when staking via staking contract directly", () => { + beforeEach(async () => { + // Infinite approval for staking contract. + await token + .connect(tokenHolder) + .approve(await tokenStaking.getAddress(), ethers.MaxUint256) + }) + + it("should stake tokens", async () => { + const tokenHolderAddress = await tokenHolder.getAddress() + const tokenBalance = await token.balanceOf(tokenHolderAddress) - it("should stake tokens", async () => { - const tokenHolderAddress = await tokenHolder.getAddress() - const tokenBalance = await token.balanceOf(tokenHolderAddress) + await expect(tokenStaking.connect(tokenHolder).stake(tokenBalance)) + .to.emit(tokenStaking, "Staked") + .withArgs(tokenHolderAddress, tokenBalance) + expect(await tokenStaking.balanceOf(tokenHolderAddress)).to.be.eq( + tokenBalance, + ) + expect(await token.balanceOf(tokenHolderAddress)).to.be.eq(0) + }) - await expect(tokenStaking.connect(tokenHolder).stake(tokenBalance)) - .to.emit(tokenStaking, "Staked") - .withArgs(tokenHolderAddress, tokenBalance) - expect(await tokenStaking.balanceOf(tokenHolderAddress)).to.be.eq( - tokenBalance, - ) - expect(await token.balanceOf(tokenHolderAddress)).to.be.eq(0) + it("should revert if the staked amount is less than required minimum", async () => { + await expect( + tokenStaking.connect(tokenHolder).stake(0), + ).to.be.revertedWith("Amount is less than minimum") + }) }) - it("should revert if the staked amount is less than required minimum", async () => { - await expect(tokenStaking.connect(tokenHolder).stake(0)) - .to.be.revertedWith("Amount is less than minimum") + describe("when staking via staking token using approve and call pattern", () => { + it("should stake tokens", async () => { + const tokenHolderAddress = await tokenHolder.getAddress() + const tokenBalance = await token.balanceOf(tokenHolderAddress) + const tokenStakingAddress = await tokenStaking.getAddress() + + await expect( + token + .connect(tokenHolder) + .approveAndCall(tokenStakingAddress, tokenBalance, "0x"), + ) + .to.emit(tokenStaking, "Staked") + .withArgs(tokenHolderAddress, tokenBalance) + expect(await tokenStaking.balanceOf(tokenHolderAddress)).to.be.eq( + tokenBalance, + ) + expect(await token.balanceOf(tokenHolderAddress)).to.be.eq(0) + }) }) }) }) From 3037f4b529e3ce7e8beaaf1d73ad0964220396c3 Mon Sep 17 00:00:00 2001 From: Rafal Czajkowski Date: Mon, 30 Oct 2023 13:25:31 +0100 Subject: [PATCH 5/8] Update staking logic Add min and max staking amount. Currently, functions that return these values have hardcoded values, but in the future we want to pull this param from "parameters" contract that stores governable params. --- core/contracts/staking/TokenStaking.sol | 18 ++++++++++++- core/test/staking/TokenStaking.test.ts | 36 ++++++++++++++++++------- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/core/contracts/staking/TokenStaking.sol b/core/contracts/staking/TokenStaking.sol index a9051040d..6441bdc46 100644 --- a/core/contracts/staking/TokenStaking.sol +++ b/core/contracts/staking/TokenStaking.sol @@ -51,8 +51,24 @@ contract TokenStaking is IReceiveApproval { _stake(msg.sender, amount); } + /// @notice Returns minimum amount of staking tokens to participate in + /// protocol. + function minimumStake() public pure returns (uint256) { + // TODO: Fetch this param from "parameters" contract that stores + // governable params. + return 1; + } + + /// @notice Returns maximum amount of staking tokens. + function maximumStake() public pure returns (uint256) { + // TODO: Fetch this param from "parameters" contract that stores + // governable params. + return 100 ether; + } + function _stake(address account, uint256 amount) private { - require(amount > 0, "Amount is less than minimum"); + require(amount >= minimumStake(), "Amount is less than minimum"); + require(amount <= maximumStake(), "Amount is greater than maxium"); require(account != address(0), "Can not be the zero address"); balanceOf[account] += amount; diff --git a/core/test/staking/TokenStaking.test.ts b/core/test/staking/TokenStaking.test.ts index 076313950..157a5fc31 100644 --- a/core/test/staking/TokenStaking.test.ts +++ b/core/test/staking/TokenStaking.test.ts @@ -38,6 +38,8 @@ describe("TokenStaking", () => { }) describe("staking", () => { + const amountToStake = WeiPerEther * 10n + describe("when staking via staking contract directly", () => { beforeEach(async () => { // Infinite approval for staking contract. @@ -48,15 +50,18 @@ describe("TokenStaking", () => { it("should stake tokens", async () => { const tokenHolderAddress = await tokenHolder.getAddress() - const tokenBalance = await token.balanceOf(tokenHolderAddress) + const tokenBalanceBeforeStake = + await token.balanceOf(tokenHolderAddress) - await expect(tokenStaking.connect(tokenHolder).stake(tokenBalance)) + await expect(tokenStaking.connect(tokenHolder).stake(amountToStake)) .to.emit(tokenStaking, "Staked") - .withArgs(tokenHolderAddress, tokenBalance) + .withArgs(tokenHolderAddress, amountToStake) expect(await tokenStaking.balanceOf(tokenHolderAddress)).to.be.eq( - tokenBalance, + amountToStake, + ) + expect(await token.balanceOf(tokenHolderAddress)).to.be.eq( + tokenBalanceBeforeStake - amountToStake, ) - expect(await token.balanceOf(tokenHolderAddress)).to.be.eq(0) }) it("should revert if the staked amount is less than required minimum", async () => { @@ -64,25 +69,36 @@ describe("TokenStaking", () => { tokenStaking.connect(tokenHolder).stake(0), ).to.be.revertedWith("Amount is less than minimum") }) + + it("should revert if the staked amount is grater than maxium stake amount", async () => { + const maxAmount = await tokenStaking.maximumStake() + + await expect( + tokenStaking.connect(tokenHolder).stake(maxAmount + 1n), + ).to.be.revertedWith("Amount is greater than maxium") + }) }) describe("when staking via staking token using approve and call pattern", () => { it("should stake tokens", async () => { const tokenHolderAddress = await tokenHolder.getAddress() - const tokenBalance = await token.balanceOf(tokenHolderAddress) + const tokenBalanceBeforeStake = + await token.balanceOf(tokenHolderAddress) const tokenStakingAddress = await tokenStaking.getAddress() await expect( token .connect(tokenHolder) - .approveAndCall(tokenStakingAddress, tokenBalance, "0x"), + .approveAndCall(tokenStakingAddress, amountToStake, "0x"), ) .to.emit(tokenStaking, "Staked") - .withArgs(tokenHolderAddress, tokenBalance) + .withArgs(tokenHolderAddress, amountToStake) expect(await tokenStaking.balanceOf(tokenHolderAddress)).to.be.eq( - tokenBalance, + amountToStake, + ) + expect(await token.balanceOf(tokenHolderAddress)).to.be.eq( + tokenBalanceBeforeStake - amountToStake, ) - expect(await token.balanceOf(tokenHolderAddress)).to.be.eq(0) }) }) }) From 607cd0cd3bdce54f00b7aa0700aebcb34da30659 Mon Sep 17 00:00:00 2001 From: Rafal Czajkowski Date: Mon, 30 Oct 2023 13:30:44 +0100 Subject: [PATCH 6/8] Rename param in `_stake` function `account` -> `staker` --- core/contracts/staking/TokenStaking.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/contracts/staking/TokenStaking.sol b/core/contracts/staking/TokenStaking.sol index 6441bdc46..fd56d4a1b 100644 --- a/core/contracts/staking/TokenStaking.sol +++ b/core/contracts/staking/TokenStaking.sol @@ -13,7 +13,7 @@ import "../shared/IReceiveApproval.sol"; contract TokenStaking is IReceiveApproval { using SafeERC20 for IERC20; - event Staked(address indexed account, uint256 amount); + event Staked(address indexed staker, uint256 amount); IERC20 internal immutable token; @@ -66,14 +66,14 @@ contract TokenStaking is IReceiveApproval { return 100 ether; } - function _stake(address account, uint256 amount) private { + function _stake(address staker, uint256 amount) private { require(amount >= minimumStake(), "Amount is less than minimum"); require(amount <= maximumStake(), "Amount is greater than maxium"); - require(account != address(0), "Can not be the zero address"); + require(staker != address(0), "Can not be the zero address"); - balanceOf[account] += amount; + balanceOf[staker] += amount; - emit Staked(account, amount); - token.safeTransferFrom(account, address(this), amount); + emit Staked(staker, amount); + token.safeTransferFrom(staker, address(this), amount); } } From a492db32a355c000ac192a95ab2be01151b43ef9 Mon Sep 17 00:00:00 2001 From: Rafal Czajkowski Date: Mon, 30 Oct 2023 13:34:00 +0100 Subject: [PATCH 7/8] Leave `TODO` in `_stake` function We should mint staking token eg. `stBTC` that represents staked token staked with Litmus. It will probably be a rebaseable ERC-20 token. --- core/contracts/staking/TokenStaking.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/core/contracts/staking/TokenStaking.sol b/core/contracts/staking/TokenStaking.sol index fd56d4a1b..86a583b04 100644 --- a/core/contracts/staking/TokenStaking.sol +++ b/core/contracts/staking/TokenStaking.sol @@ -73,6 +73,7 @@ contract TokenStaking is IReceiveApproval { balanceOf[staker] += amount; + // TODO: Mint stBTC token. emit Staked(staker, amount); token.safeTransferFrom(staker, address(this), amount); } From 1ecc148997c03a24f636c8523afa33a78f14c9de Mon Sep 17 00:00:00 2001 From: Rafal Czajkowski Date: Thu, 2 Nov 2023 09:49:59 +0100 Subject: [PATCH 8/8] Fix linting errors --- core/contracts/staking/TokenStaking.sol | 4 ++-- core/contracts/test/TestToken.sol | 1 - core/deploy/01_deploy_acre.ts | 2 +- core/test/staking/TokenStaking.test.ts | 15 ++++----------- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/core/contracts/staking/TokenStaking.sol b/core/contracts/staking/TokenStaking.sol index 86a583b04..9866ce02b 100644 --- a/core/contracts/staking/TokenStaking.sol +++ b/core/contracts/staking/TokenStaking.sol @@ -13,12 +13,12 @@ import "../shared/IReceiveApproval.sol"; contract TokenStaking is IReceiveApproval { using SafeERC20 for IERC20; - event Staked(address indexed staker, uint256 amount); - IERC20 internal immutable token; mapping(address => uint256) public balanceOf; + event Staked(address indexed staker, uint256 amount); + constructor(IERC20 _token) { require( address(_token) != address(0), diff --git a/core/contracts/test/TestToken.sol b/core/contracts/test/TestToken.sol index c061cf278..943d20bd6 100644 --- a/core/contracts/test/TestToken.sol +++ b/core/contracts/test/TestToken.sol @@ -5,7 +5,6 @@ pragma solidity 0.8.20; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "../shared/IReceiveApproval.sol"; - contract Token is ERC20 { constructor() ERC20("Test Token", "TEST") {} diff --git a/core/deploy/01_deploy_acre.ts b/core/deploy/01_deploy_acre.ts index 3266a1986..6b3121222 100644 --- a/core/deploy/01_deploy_acre.ts +++ b/core/deploy/01_deploy_acre.ts @@ -1,7 +1,7 @@ import type { HardhatRuntimeEnvironment } from "hardhat/types" import type { DeployFunction } from "hardhat-deploy/types" -const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { +const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { const { deployments } = hre const { log } = deployments diff --git a/core/test/staking/TokenStaking.test.ts b/core/test/staking/TokenStaking.test.ts index 157a5fc31..c6b458bf9 100644 --- a/core/test/staking/TokenStaking.test.ts +++ b/core/test/staking/TokenStaking.test.ts @@ -1,12 +1,12 @@ import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers" import { ethers } from "hardhat" import { expect } from "chai" -import { Token, TokenStaking } from "../../typechain" import { WeiPerEther } from "ethers" import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers" +import type { Token, TokenStaking } from "../../typechain" async function tokenStakingFixture() { - const [deployer, tokenHolder] = await ethers.getSigners() + const [_, tokenHolder] = await ethers.getSigners() const StakingToken = await ethers.getContractFactory("Token") const token = await StakingToken.deploy() @@ -26,15 +26,8 @@ describe("TokenStaking", () => { let tokenHolder: HardhatEthersSigner beforeEach(async () => { - const { - tokenStaking: _tokenStaking, - token: _token, - tokenHolder: _tokenHolder, - } = await loadFixture(tokenStakingFixture) - - tokenStaking = _tokenStaking - token = _token - tokenHolder = _tokenHolder + ;({ tokenStaking, token, tokenHolder } = + await loadFixture(tokenStakingFixture)) }) describe("staking", () => {