From 834bebecb37d0ee07b7f9230a3dd392e067065a3 Mon Sep 17 00:00:00 2001 From: amaqkkg <16919110+amaqkkg@users.noreply.github.com> Date: Fri, 3 Jan 2025 23:49:03 +0700 Subject: [PATCH] initial commit --- .gitmodules | 6 + README.md | 51 +-------- lib/openzeppelin-contracts | 1 + lib/openzeppelin-contracts-upgradeable | 1 + script/Counter.s.sol | 19 ---- src/Bank.sol | 0 src/Counter.sol | 14 --- src/IDRCoin.sol | 152 +++++++++++++++++++++++++ src/interfaces/IUSDT.sol | 12 ++ test/Counter.t.sol | 24 ---- 10 files changed, 174 insertions(+), 106 deletions(-) create mode 160000 lib/openzeppelin-contracts create mode 160000 lib/openzeppelin-contracts-upgradeable delete mode 100644 script/Counter.s.sol create mode 100644 src/Bank.sol delete mode 100644 src/Counter.sol create mode 100644 src/IDRCoin.sol create mode 100644 src/interfaces/IUSDT.sol delete mode 100644 test/Counter.t.sol diff --git a/.gitmodules b/.gitmodules index 888d42d..a3b6416 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,9 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "lib/openzeppelin-contracts"] + path = lib/openzeppelin-contracts + url = https://github.com/openzeppelin/openzeppelin-contracts +[submodule "lib/openzeppelin-contracts-upgradeable"] + path = lib/openzeppelin-contracts-upgradeable + url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable diff --git a/README.md b/README.md index 9265b45..7efc523 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,7 @@ -## Foundry +## IDRCoin -**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** +Disclaimer: this is a part of Penerbangan Pertama educational content, DO NOT USE ON PRODUCTION as this project contains intentional bug. -Foundry consists of: - -- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). -- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. -- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. -- **Chisel**: Fast, utilitarian, and verbose solidity REPL. - -## Documentation - -https://book.getfoundry.sh/ ## Usage @@ -27,40 +17,3 @@ $ forge build $ forge test ``` -### Format - -```shell -$ forge fmt -``` - -### Gas Snapshots - -```shell -$ forge snapshot -``` - -### Anvil - -```shell -$ anvil -``` - -### Deploy - -```shell -$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key -``` - -### Cast - -```shell -$ cast -``` - -### Help - -```shell -$ forge --help -$ anvil --help -$ cast --help -``` diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts new file mode 160000 index 0000000..69c8def --- /dev/null +++ b/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit 69c8def5f222ff96f2b5beff05dfba996368aa79 diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable new file mode 160000 index 0000000..fa52531 --- /dev/null +++ b/lib/openzeppelin-contracts-upgradeable @@ -0,0 +1 @@ +Subproject commit fa525310e45f91eb20a6d3baa2644be8e0adba31 diff --git a/script/Counter.s.sol b/script/Counter.s.sol deleted file mode 100644 index cdc1fe9..0000000 --- a/script/Counter.s.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script, console} from "forge-std/Script.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterScript is Script { - Counter public counter; - - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - counter = new Counter(); - - vm.stopBroadcast(); - } -} diff --git a/src/Bank.sol b/src/Bank.sol new file mode 100644 index 0000000..e69de29 diff --git a/src/Counter.sol b/src/Counter.sol deleted file mode 100644 index aded799..0000000 --- a/src/Counter.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -contract Counter { - uint256 public number; - - function setNumber(uint256 newNumber) public { - number = newNumber; - } - - function increment() public { - number++; - } -} diff --git a/src/IDRCoin.sol b/src/IDRCoin.sol new file mode 100644 index 0000000..cc4b596 --- /dev/null +++ b/src/IDRCoin.sol @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.28; +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {IUSDT} from "./interfaces/IUSDT.sol"; + +contract IDRCoin is ERC20 { + // mapping + mapping(address => uint256) balances; + mapping(address owner => mapping(address spender => uint256)) allowances; + + // address state + address admin; + address taxCollector; + IUSDT usdt; + + // constant + // convertion rate from USDT to IDR + uint256 constant CONVERSION_RATE = 16000; + + // enforce ppn 12% for ALL transaction involving IDRC token + uint256 constant TAX = 12; + uint256 constant DENOMINATOR = 100; + + modifier onlyAdmin() { + if (msg.sender != admin) { + revert notAdmin(); + } + _; + } + + // error + error notAdmin(); + error insufficientBalance(); + error insufficientAllowances(); + error addressZero(); + + // event + + // constructor + constructor( + address _admin, + address _taxCollector + ) ERC20("IDRCoin", "IDRC") { + admin = _admin; + taxCollector = _taxCollector; + } + + // external/public function + + function convertUSDtoIDR(uint256 amountInUSD) external { + usdt.transfer(address(this), amountInUSD); + uint256 amountInIDR = amountInUSD * CONVERSION_RATE * decimals(); + _mint(msg.sender, amountInIDR); + } + + function approve( + address _spender, + uint256 amount + ) public override returns (bool) { + allowances[msg.sender][_spender] = amount; + return true; + } + + function transfer( + address _receiver, + uint256 _amount + ) public override returns (bool) { + if (balanceOf(msg.sender) < _amount) { + revert insufficientBalance(); + } + if (_receiver == address(0)) { + revert addressZero(); + } + transfer_(msg.sender, _receiver, _amount); + return true; + } + + function transferFrom( + address _owner, + address _receiver, + uint256 amount + ) public override returns (bool) { + if (allowances[msg.sender][_owner] < amount) { + revert insufficientAllowances(); + } + if (_receiver == address(0)) { + revert addressZero(); + } + transfer_(_owner, _receiver, amount); + return true; + } + + // internal function + + function mint_(address _addr, uint256 amount) internal { + // calculating the tax amount and then collect it + uint256 tax = (amount * TAX) / DENOMINATOR; + balances[_addr] += amount - tax; + balances[taxCollector] += tax; + } + + function transfer_( + address _sender, + address _receiver, + uint256 _amount + ) internal { + uint256 balanceSenderBefore = balanceOf(_sender); + uint256 balanceReceiverBefore = balanceOf(_receiver); + + balances[_sender] = balanceSenderBefore - _amount; + balances[_receiver] = balanceReceiverBefore + _amount; + + // revert if the _amount is zero. we dont want user to waste gas + require( + balances[_sender] != balanceSenderBefore || + balances[_receiver] != balanceReceiverBefore, + "cant transfer 0 amount" + ); + } + + // view function + + function balanceOf( + address _addr + ) public view override returns (uint256 amount) { + return balances[_addr]; + } + + // setter/admin function + + function setUSDT(address _usdt) external onlyAdmin { + usdt = IUSDT(_usdt); + } + + function changeOwner(address admin) external onlyAdmin { + admin = admin; + } + + // collect USDT for public goods purpose + + function withdrawUSDT(address _addr) external onlyAdmin { + // transfer usdt to admin specified address + uint256 amount = usdt.balanceOf(address(this)); + usdt.transfer(_addr, amount); + } + + // burn ALL corruptor IDRC with this function + function burn(address _account, uint256 _amount) public onlyAdmin { + transfer_(_account, address(0), _amount); + } +} diff --git a/src/interfaces/IUSDT.sol b/src/interfaces/IUSDT.sol new file mode 100644 index 0000000..9e3a868 --- /dev/null +++ b/src/interfaces/IUSDT.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.28; + +interface IUSDT { + function decimals() external returns (uint); + + function balanceOf(address who) external returns (uint); + + function transfer(address to, uint value) external; + + function approve(address spender, uint value) external; +} diff --git a/test/Counter.t.sol b/test/Counter.t.sol deleted file mode 100644 index 54b724f..0000000 --- a/test/Counter.t.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Test, console} from "forge-std/Test.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterTest is Test { - Counter public counter; - - function setUp() public { - counter = new Counter(); - counter.setNumber(0); - } - - function test_Increment() public { - counter.increment(); - assertEq(counter.number(), 1); - } - - function testFuzz_SetNumber(uint256 x) public { - counter.setNumber(x); - assertEq(counter.number(), x); - } -}