8-3. rewardPerBlock 변경 기능 (setRewardPerBlock)

동동주·2025년 11월 2일


Todo :
0. 추가하려는 내용
1. access control module 사용
2. 기타 수정사항




0. 추가하려는 내용

사용자의 증가, 시장 상황 변화 등으로 인해 rewardPerBlock을 변경해야 하는 상황에 필요한 setRewardPerBlock 기능을 만드려고 한다.

contracts/TinyBank.sol

contract TinyBank {
    event Staked(address, uint256);
    event Withdraw(uint256 amount, address to);

    IMyToken public stakingToken; 

    mapping(address => uint256) public lastClaimedBlock; 
    
  // 추가 및 변경
    uint256 defaultRewardPerBlock =  1*10 ** 18; //추가
    uint256 rewardPerBlock; //변경
    
    mapping(address => uint256) public staked; 
    uint256 public totalStaked; 

    

    constructor(IMyToken _stakingToken) { 
        stakingToken = _stakingToken;
        rewardPerBlock = defaultRewardPerBlock; //추가
    }

  //추가
    function setRewardPerBlock(uint256 _amount) external {
        rewardPerBlock = _amount;
    }

이렇게 코드를 추가하면 리워드를 변경할 수는 있으나,
이것 역시 이전 mint() 와 동일하게 외부에서 악의적으로 사용할 수 있다는 문제가 생긴다.

test/TinyBank.ts

describe("reward", async () => {
  ...
it("Should revert when changing rewardPerBlock by hacker", async () => {
      const hacker = signers[3];
      const rewardToChange = hre.ethers.parseUnits("10000", DECIMALS);
      await expect(
        tinyBankC.connect(hacker).setRewardPerBlock(rewardToChange),
      ).to.be.revertedWith("You are not authorized to manage this contract");
    });
  ..
}

따라서 이것 역시 권한이 없으면 사용하지 못하게 하는 테스트를 만들고, 성공하도록 코드를 만들어보자




1. access control module 사용

contracts/TinyBank.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import "./ManagedAccess.sol"; //추가

...

contract TinyBank is ManagedAccess { //상속
    event Staked(address, uint256);
    event Withdraw(uint256 amount, address to);

    IMyToken public stakingToken; 

    mapping(address => uint256) public lastClaimedBlock; 
    
    uint256 defaultRewardPerBlock =  1*10 ** 18;
    uint256 rewardPerBlock;
    
    mapping(address => uint256) public staked; 
    uint256 public totalStaked; 

    

    constructor(IMyToken _stakingToken) ManagedAccess(msg.sender, msg.sender) { //생성자
        stakingToken = _stakingToken;
        rewardPerBlock = defaultRewardPerBlock;
    }

    function setRewardPerBlock(uint256 _amount) external onlyManager { //ManagedAccess 사용
        rewardPerBlock = _amount;
    }

지난번에 했던 것처럼 똑같이 해주면 된다.
( 8-2. mint 함수 접근권한 제한하기 (access control) )

만들어둔 access control module를 사용한다.
ManagedAccess를 상속받고 생성자 써주고,
권한을 제한하려는 setRewardPerBlock에 onlyManager를 작성해준다.




2. 기타 수정사항

이 ManagedAccess가 다양한 함수에 사용되니까 revertedWith 문구를 좀 더 일반적인 걸로 바꿔주는 것이 좋다고 한다.

contracts/ManagedAccess.sol

modifier onlyManager {
        require(
            msg.sender == manager, 
        "You are not authorized to manage this contract" //변경
        ); 
        _;
    }

token --> contract로 변경해줬다.


그리고 이전 문구를 사용하던 부분도 변경해줘야 하므로

test/MyToken.ts

  describe("Mint", () => {
  
  ....
  
    it("should return or revert when minting infinitly", async () => {
      const hacker = signers[2];
      const mintingAgainAmount = hre.ethers.parseUnits("10000", DECIMALS);
      await expect(
        myTokenC.connect(hacker).mint(mintingAgainAmount, hacker.address),
      ).to.be.revertedWith("You are not authorized to manage this contract"); 
      //문구 변경
    });
  });

이전에 사용했던 MyToken에서 Mint 테스트 부분도 변경해준다.




git commit

❯ git add . 

❯ git commit

-m 없이 git commit 만 입력하니까 편집창이 뜨는데,
거기에

feat(TinyBank): add function to change rewardPerBlock

- Apply AccessManager to TinyBank for onlyManager restriction.
- Rename onlyManager modifier message to be more generic.
- Add test for malicious attempt to change rewardPerBlock.

이렇게 메세지 입력해주고

들어간 편집창이 nano 라는 거길래.. (vim만 써봄)
Ctrl+O(저장) > (File Name to Write) Enter > Ctrl+X
이 방식으로 저장하고 나왔다.

profile
배운 내용 정리&기록, 스크랩

0개의 댓글