Skip to main content

Smart Contracts (Solidity + Foundry)

The on-chain side is a Foundry project with two contracts.

RupiahToken (IDRT)

A standard ERC20 (18 decimals) — the “money” you save. Includes a mint(to, amount) faucet so anyone can grab test tokens.

Vault

A savings “celengan”: deposit(amount) / withdraw(amount), tracking each address’s balance 1:1.

The contracts

src/RupiahToken.sol (excerpt)
contract RupiahToken is ERC20, Ownable {
    constructor() ERC20("Rupiah Token", "IDRT") Ownable(msg.sender) {
        _mint(msg.sender, 1_000_000 * 10 ** 18);
    }

    // Open faucet — anyone can mint test tokens.
    function mint(address to, uint256 amount) external {
        _mint(to, amount);
    }
}
src/Vault.sol (excerpt)
contract Vault {
    IERC20 public immutable token;
    mapping(address => uint256) public balances;
    uint256 public totalDeposits;

    constructor(IERC20 _token) { token = _token; }

    function deposit(uint256 amount) external {
        // Needs prior token.approve(vault, amount) — pulls via transferFrom.
        token.transferFrom(msg.sender, address(this), amount);
        balances[msg.sender] += amount;
        totalDeposits += amount;
    }

    function withdraw(uint256 amount) external {
        require(balances[msg.sender] >= amount, "Vault: saldo tidak cukup");
        balances[msg.sender] -= amount;   // effects before interaction
        totalDeposits -= amount;
        token.transfer(msg.sender, amount);
    }
}
deposit uses the approve → transferFrom pattern, and withdraw follows Checks-Effects-Interactions (state updated before sending tokens) to avoid reentrancy.

Get the project

git clone --recurse-submodules https://github.com/DevWeb3Jogja/workshop-amikom.git
cd workshop-amikom
forge build

Deploy to Sepolia

Put your deployer key in an env file (a throwaway test key with Sepolia ETH):
.env
WALLET_PK=0xyour_private_key
ETHERSCAN_API_KEY=your_etherscan_key
Never commit .env or use a key holding real funds. Add .env to .gitignore.
Deploy both contracts (token first, then the Vault pointing to it):
source .env
export RPC=https://ethereum-sepolia-rpc.publicnode.com

forge script script/Deploy.s.sol \
  --rpc-url $RPC --private-key $WALLET_PK --broadcast
Foundry prints the deployed addresses:
RupiahToken : 0x7C78582DEa2a03d982D5fb0975fb4895E731dD05
Vault       : 0x8E81aB5f111670D732D961e892E94aCFcD593b07

Verify on Etherscan

forge verify-contract <RUPIAH_TOKEN_ADDRESS> src/RupiahToken.sol:RupiahToken \
  --chain sepolia --etherscan-api-key $ETHERSCAN_API_KEY --watch

forge verify-contract <VAULT_ADDRESS> src/Vault.sol:Vault \
  --chain sepolia --etherscan-api-key $ETHERSCAN_API_KEY \
  --constructor-args $(cast abi-encode "constructor(address)" <RUPIAH_TOKEN_ADDRESS>) \
  --watch

Quick sanity check with cast

cast call <RUPIAH_TOKEN_ADDRESS> "symbol()(string)" --rpc-url $RPC      # "IDRT"
cast call <VAULT_ADDRESS> "token()(address)" --rpc-url $RPC            # == token
cast call <VAULT_ADDRESS> "totalDeposits()(uint256)" --rpc-url $RPC

Next: Frontend Integration

Wire the contracts into the React app.