initial commit
This commit is contained in:
parent
a390ba8f99
commit
834bebecb3
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -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
|
||||
|
||||
51
README.md
51
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 <your_rpc_url> --private-key <your_private_key>
|
||||
```
|
||||
|
||||
### Cast
|
||||
|
||||
```shell
|
||||
$ cast <subcommand>
|
||||
```
|
||||
|
||||
### Help
|
||||
|
||||
```shell
|
||||
$ forge --help
|
||||
$ anvil --help
|
||||
$ cast --help
|
||||
```
|
||||
|
||||
1
lib/openzeppelin-contracts
Submodule
1
lib/openzeppelin-contracts
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 69c8def5f222ff96f2b5beff05dfba996368aa79
|
||||
1
lib/openzeppelin-contracts-upgradeable
Submodule
1
lib/openzeppelin-contracts-upgradeable
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit fa525310e45f91eb20a6d3baa2644be8e0adba31
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
0
src/Bank.sol
Normal file
0
src/Bank.sol
Normal file
@ -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++;
|
||||
}
|
||||
}
|
||||
152
src/IDRCoin.sol
Normal file
152
src/IDRCoin.sol
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
12
src/interfaces/IUSDT.sol
Normal file
12
src/interfaces/IUSDT.sol
Normal file
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user