> ## Documentation Index
> Fetch the complete documentation index at: https://web3docs.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Smart Contracts

> The RupiahToken + Vault contracts, and deploying them to Sepolia

# Smart Contracts (Solidity + Foundry)

The on-chain side is a [Foundry](https://book.getfoundry.sh) project with two
contracts.

<CardGroup cols={2}>
  <Card title="RupiahToken (IDRT)" icon="coins">
    A standard **ERC20** (18 decimals) — the "money" you save. Includes a
    `mint(to, amount)` faucet so anyone can grab test tokens.
  </Card>

  <Card title="Vault" icon="vault">
    A savings "celengan": `deposit(amount)` / `withdraw(amount)`, tracking each
    address's balance 1:1.
  </Card>
</CardGroup>

## The contracts

```solidity title="src/RupiahToken.sol (excerpt)" theme={null}
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);
    }
}
```

```solidity title="src/Vault.sol (excerpt)" theme={null}
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);
    }
}
```

<Info>
  `deposit` uses the **approve → transferFrom** pattern, and `withdraw` follows
  **Checks-Effects-Interactions** (state updated before sending tokens) to avoid
  reentrancy.
</Info>

## Get the project

```bash theme={null}
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):

```bash title=".env" theme={null}
WALLET_PK=0xyour_private_key
ETHERSCAN_API_KEY=your_etherscan_key
```

<Warning>
  Never commit `.env` or use a key holding real funds. Add `.env` to
  `.gitignore`.
</Warning>

Deploy both contracts (token first, then the Vault pointing to it):

```bash theme={null}
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

```bash theme={null}
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`

```bash theme={null}
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
```

<Card title="Next: Frontend Integration" icon="arrow-right" href="/evm/workshop-amikom/05-frontend-integration" horizontal>
  Wire the contracts into the React app.
</Card>
