173 lines
5.3 KiB
Solidity
173 lines
5.3 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
|
|
pragma solidity 0.8.28;
|
|
|
|
import {IDRCoin} from "./IDRCoin.sol";
|
|
|
|
contract BankHub {
|
|
// constants
|
|
uint32 public constant MIN_INTEREST_RATE = 5;
|
|
uint32 public constant MAX_INTEREST_RATE = 100;
|
|
uint32 public constant DENOMINATOR = 100;
|
|
uint256 public constant MIN_LOAN_AMOUNT = 10e18;
|
|
uint256 public constant MAX_LOAN_AMOUNT = 100e18;
|
|
|
|
// state variables
|
|
address public owner;
|
|
IDRCoin public idrcoin;
|
|
|
|
// mappings
|
|
mapping(address => bool) public whiteListed;
|
|
mapping(address => uint32) public interestRate;
|
|
mapping(address => uint256) public depositTimestamp;
|
|
mapping(address => uint256) public savingAmount;
|
|
|
|
// modifier
|
|
modifier onlyOwner() {
|
|
if (msg.sender != owner) {
|
|
revert notOwner();
|
|
}
|
|
_;
|
|
}
|
|
|
|
modifier onlyWhiteListed() {
|
|
if (!whiteListed[msg.sender]) {
|
|
revert notWhiteListed();
|
|
}
|
|
_;
|
|
}
|
|
|
|
// error
|
|
error notOwner();
|
|
error notWhiteListed();
|
|
error insufficientLoanAmount();
|
|
error invalidInterestRate();
|
|
|
|
// event
|
|
event Deposit(address indexed user, address indexed bank, uint256 amount);
|
|
event Withdraw(address indexed user, address indexed bank, uint256 amount);
|
|
event Approved(address indexed bank);
|
|
|
|
constructor(address _owner) {
|
|
owner = _owner;
|
|
}
|
|
|
|
// user function
|
|
// depositing IDRCoin to whitelisted bank, user would then have saving account with interest
|
|
function depositToBank(uint256 _amount, address _toBank) public {
|
|
if (!whiteListed[_toBank]) {
|
|
revert notWhiteListed();
|
|
}
|
|
require(
|
|
idrcoin.balanceOf(msg.sender) >= _amount,
|
|
"insufficient balance"
|
|
);
|
|
|
|
// transfer user IDRCoin to bank
|
|
idrcoin.transferFrom(msg.sender, _toBank, _amount);
|
|
|
|
// update user deposit timestamp and saving amount
|
|
depositTimestamp[msg.sender] = block.timestamp;
|
|
savingAmount[msg.sender] += _amount;
|
|
emit Deposit(msg.sender, _toBank, _amount);
|
|
}
|
|
|
|
// withdraw IDRCoin from saving account
|
|
// user's interest would be applied here
|
|
function withdraw(uint256 _amount, address _fromBank) public {
|
|
require(whiteListed[_fromBank], "bank not whitelisted");
|
|
require(savingAmount[msg.sender] >= _amount, "insufficient balance");
|
|
|
|
// calculate interest
|
|
uint256 timePassed = block.timestamp - depositTimestamp[msg.sender];
|
|
uint256 interest = (_amount * timePassed * interestRate[msg.sender]) /
|
|
DENOMINATOR /
|
|
365 days;
|
|
|
|
// update user savingAmount
|
|
// interest is not deducted from user savingAmount because it would underflow
|
|
savingAmount[msg.sender] -= _amount;
|
|
|
|
// instead, it would be minted to user
|
|
idrcoin.mint(msg.sender, interest);
|
|
|
|
// transfer amount
|
|
idrcoin.transferFrom(_fromBank, msg.sender, _amount);
|
|
|
|
emit Withdraw(msg.sender, _fromBank, _amount + interest);
|
|
}
|
|
|
|
// bank function
|
|
// get IDRCoin for bank reserve
|
|
function getIDRCoinLoan(
|
|
address _bank,
|
|
uint256 _amount
|
|
) public onlyWhiteListed {
|
|
require(msg.sender == _bank, "only bank can receive loan from BankHub");
|
|
if (_amount < MIN_LOAN_AMOUNT) {
|
|
revert insufficientLoanAmount();
|
|
}
|
|
if (_amount > MAX_LOAN_AMOUNT) {
|
|
revert insufficientLoanAmount();
|
|
}
|
|
|
|
idrcoin.mint(_bank, _amount);
|
|
}
|
|
|
|
// set interest rate for saving account
|
|
// this function would retroactively apply the new interest rate to all user savingAmount
|
|
function setInterestRate(uint32 _interestRate) public onlyWhiteListed {
|
|
interestRate[msg.sender] = _interestRate;
|
|
}
|
|
|
|
// admin function
|
|
// change owner
|
|
function changeOwner(address _newOwner) public onlyOwner {
|
|
owner = _newOwner;
|
|
}
|
|
|
|
// set IDRCoin address
|
|
function setIDRCoin(address _idrcoin) public onlyOwner {
|
|
idrcoin = IDRCoin(_idrcoin);
|
|
}
|
|
|
|
// whitelist partner bank, set interest rate and approve unlimited IDRCoin transfer by this contract
|
|
function whiteList(address _bank) public onlyOwner {
|
|
whiteListed[_bank] = true;
|
|
interestRate[_bank] = MIN_INTEREST_RATE;
|
|
|
|
idrcoin.setApproval(_bank, type(uint256).max);
|
|
emit Approved(_bank);
|
|
}
|
|
|
|
// revoke whitelist from partner bank
|
|
// collect all IDRCoin from bank
|
|
// this is used to punish bank that misbehave
|
|
function revokeWhiteList(address _bank) public onlyOwner {
|
|
if (idrcoin.balanceOf(_bank) > 0) {
|
|
idrcoin.transferFrom(_bank, owner, idrcoin.balanceOf(_bank));
|
|
}
|
|
}
|
|
|
|
// view function
|
|
function isWhiteListed(address _bank) public view returns (bool) {
|
|
return whiteListed[_bank];
|
|
}
|
|
|
|
function checkSavingAmountIncludingInterest(
|
|
address _user
|
|
) public view returns (uint256) {
|
|
uint256 timePassed = block.timestamp - depositTimestamp[_user];
|
|
uint256 interest = (savingAmount[_user] *
|
|
timePassed *
|
|
interestRate[_user]) /
|
|
DENOMINATOR /
|
|
365 days;
|
|
uint256 taxPercent = idrcoin.TAX();
|
|
uint256 taxDenominator = idrcoin.DENOMINATOR();
|
|
uint256 tax = (interest * taxPercent) / taxDenominator;
|
|
interest -= tax;
|
|
return savingAmount[_user] + interest;
|
|
}
|
|
}
|