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"]
|
[submodule "lib/forge-std"]
|
||||||
path = lib/forge-std
|
path = lib/forge-std
|
||||||
url = https://github.com/foundry-rs/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
|
## Usage
|
||||||
|
|
||||||
@ -27,40 +17,3 @@ $ forge build
|
|||||||
$ forge test
|
$ 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