JPYC, Solidity, Polygon, Hardhat, SmartContract, Web3, Blockchain

Celebrating ¥100M Issued! A Practical Guide to Working with JPYC in Smart Contracts (Using Solidity, Hardhat, and Polygon) - Technical Perspective

10 Nov 2025

Why Dive into JPYC Right Now?

JPYC, the stablecoin tied to the Japanese Yen, hit the scene on October 27, 2025. It's blown up fast in Japan's Web3 world, crossing ¥100 million in issuance within just six days.

Up until now, if you were building dApps in Japan, you were stuck dealing with unpredictable cryptos like ETH or MATIC, or relying on overseas stablecoins like USDC. That made it tricky to create yen-based services without extra headaches.

But JPYC changes the game—it's officially an "Electronic Payment Instrument" under Japan's Payment Services Act. This means smoother, safer Web3 apps that feel natural for yen users. In this guide, I'll walk you through interacting with JPYC from a smart contract, with real code snippets. We'll use Polygon for its cheap fees and quick transactions, plus Hardhat and Solidity to build a basic contract for depositing and withdrawing JPYC.

Quick Heads-Up on Versions

I tested this on November 6, 2025. Web3 stuff moves quick, so updates to tools might break things later. Stick to these versions for best results:

  • Hardhat: 2.22.4

  • @nomicfoundation/hardhat-toolbox: 5.0.0

  • @openzeppelin/contracts: 5.0.2

  • dotenv: 16.4.5

Pop them into your package.json and run npm install to match.

What You'll Need to Get Started

  • Node.js (v18 or up works best)

  • A code editor like VS Code

  • MetaMask wallet extension with an account set up

  • Some MATIC on Polygon for gas fees (deploying and transacting)

  • A bit of JPYC for testing—grab a few hundred yen worth from the "JPYC EX" on the official site and send it to your MetaMask on Polygon

Step 1: Setting Up Hardhat

Fire up your terminal, make a folder, and kick off a Hardhat project:

mkdir jpyc-sc-tutorial
cd jpyc-sc-tutorial
npx hardhat

Answer the prompts like this:

  • What do you want to do? → Create a JavaScript project

  • Hardhat project root: → Hit Enter for default

  • Add a .gitignore? → yes

  • Install dependencies with npm? → yes

Then install these:

npm install @openzeppelin/contracts@5.0.2 dotenv@16.4.5

Create a .env file in the root and add:

POLYGON_RPC_URL="YOUR_ALCHEMY_OR_INFURA_POLYGON_RPC_URL"
PRIVATE_KEY="YOUR_METAMASK_PRIVATE_KEY"
  • Swap in your Polygon RPC from Alchemy or Infura.

  • Use your MetaMask private key here—but keep it secret, seriously.

Update hardhat.config.js:

require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config();

module.exports = {
  solidity: "0.8.20",
  networks: {
    polygon: {
      url: process.env.POLYGON_RPC_URL || "",
      accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
    },
  },
};

Step 2: Building the Smart Contract

In the contracts folder, ditch the sample Lock.sol and make JPYCHandler.sol:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract JPYCHandler is Ownable {
    IERC20 public immutable jpycToken;

    event Deposit(address indexed user, uint256 amount);
    event Withdraw(address indexed owner, uint256 amount);

    address constant JPYC_POLYGON_ADDRESS = 0x431D5dfF03120AFA4bDf332c61A6e1766eF37BDB;

    constructor() Ownable(msg.sender) {
        jpycToken = IERC20(JPYC_POLYGON_ADDRESS);
    }

    /**
     * @dev Lets a user deposit JPYC into the contract.
     * @param _amount Amount of JPYC (with 18 decimals in mind).
     * Note: User needs to approve this contract first to spend their JPYC.
     */
    function depositJPYC(uint256 _amount) external {
        require(_amount > 0, "Amount must be greater than zero");
        uint256 allowance = jpycToken.allowance(msg.sender, address(this));
        require(allowance >= _amount, "Check the token allowance");
        bool success = jpycToken.transferFrom(msg.sender, address(this), _amount);
        require(success, "Transfer failed");
        emit Deposit(msg.sender, _amount);
    }

    /**
     * @dev Owner can pull out all JPYC from the contract.
     */
    function withdrawAllJPYC() external onlyOwner {
        uint256 balance = getContractBalance();
        require(balance > 0, "No JPYC to withdraw");
        jpycToken.transfer(owner(), balance);
        emit Withdraw(owner(), balance);
    }

    /**
     * @dev Check the contract's JPYC balance.
     * @return Balance in wei-like units.
     */
    function getContractBalance() public view returns (uint256) {
        return jpycToken.balanceOf(address(this));
    }
}

Step 3: Deploying and Interacting

Now let's get it on-chain and test it.

  1. Deployment Script

Tweak scripts/deploy.js:

const hre = require("hardhat");

async function main() {
  const jpycHandler = await hre.ethers.deployContract("JPYCHandler");
  await jpycHandler.waitForDeployment();
  console.log(`JPYCHandler deployed to: ${jpycHandler.target}`);
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Run it:

npx hardhat run scripts/deploy.js --network polygon

Grab the deployed address from the console.

  1. Interaction Script

Add scripts/interact.js:

const { ethers } = require("hardhat");

// CONFIG
const JPYC_HANDLER_ADDRESS = "YOUR_DEPLOYED_CONTRACT_ADDRESS";
const JPYC_TOKEN_ADDRESS = "0x431D5dfF03120AFA4bDf332c61A6e1766eF37BDB";
const DEPOSIT_AMOUNT = ethers.parseUnits("10", 18); // JPYC has 18 decimals

async function main() {
  const [signer] = await ethers.getSigners();
  console.log("Using wallet:", signer.address);

  const jpycHandler = await ethers.getContractAt("JPYCHandler", JPYC_HANDLER_ADDRESS, signer);
  const jpycToken = await ethers.getContractAt("IERC20", JPYC_TOKEN_ADDRESS, signer);

  console.log("\\nApproving spend...");
  const approveTx = await jpycToken.approve(JPYC_HANDLER_ADDRESS, DEPOSIT_AMOUNT);
  await approveTx.wait();
  console.log("Approved. Tx:", approveTx.hash);

  console.log("\\nDepositing...");
  const depositTx = await jpycHandler.depositJPYC(DEPOSIT_AMOUNT);
  await depositTx.wait();
  console.log("Deposited. Tx:", depositTx.hash);

  const contractBalance = await jpycHandler.getContractBalance();
  console.log("\\nCheck balance:", ethers.formatUnits(contractBalance, 18) + " JPYC");
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Swap in your contract address, then run:

npx hardhat run scripts/interact.js --network polygon

You should see the approval, deposit, and a balance of 10 JPYC.

Wrapping Up and Ideas for More

That's the basics for handling JPYC in a contract. The approve-then-transferFrom pattern is key for any ERC20 work—get comfy with it.

This simple setup can grow into cool stuff like:

  • An e-commerce payment system

  • Tipping for creators on blogs

  • Stable in-game money for Web3 games

With JPYC here, yen-focused Web3 projects have tons of potential. Grab this code, tweak it, and build something awesome.

I've put this guide together to give you a solid starting point for building with JPYC. Web3 development can sometimes throw a curveball, especially with different versions and environments.

If you follow the steps but still run into any trouble, or just have a question about the code, please feel free to reach out. I'd be happy to help clarify things. Happy building

Why Dive into JPYC Right Now?

JPYC, the stablecoin tied to the Japanese Yen, hit the scene on October 27, 2025. It's blown up fast in Japan's Web3 world, crossing ¥100 million in issuance within just six days.

Up until now, if you were building dApps in Japan, you were stuck dealing with unpredictable cryptos like ETH or MATIC, or relying on overseas stablecoins like USDC. That made it tricky to create yen-based services without extra headaches.

But JPYC changes the game—it's officially an "Electronic Payment Instrument" under Japan's Payment Services Act. This means smoother, safer Web3 apps that feel natural for yen users. In this guide, I'll walk you through interacting with JPYC from a smart contract, with real code snippets. We'll use Polygon for its cheap fees and quick transactions, plus Hardhat and Solidity to build a basic contract for depositing and withdrawing JPYC.

Quick Heads-Up on Versions

I tested this on November 6, 2025. Web3 stuff moves quick, so updates to tools might break things later. Stick to these versions for best results:

  • Hardhat: 2.22.4

  • @nomicfoundation/hardhat-toolbox: 5.0.0

  • @openzeppelin/contracts: 5.0.2

  • dotenv: 16.4.5

Pop them into your package.json and run npm install to match.

What You'll Need to Get Started

  • Node.js (v18 or up works best)

  • A code editor like VS Code

  • MetaMask wallet extension with an account set up

  • Some MATIC on Polygon for gas fees (deploying and transacting)

  • A bit of JPYC for testing—grab a few hundred yen worth from the "JPYC EX" on the official site and send it to your MetaMask on Polygon

Step 1: Setting Up Hardhat

Fire up your terminal, make a folder, and kick off a Hardhat project:

mkdir jpyc-sc-tutorial
cd jpyc-sc-tutorial
npx hardhat

Answer the prompts like this:

  • What do you want to do? → Create a JavaScript project

  • Hardhat project root: → Hit Enter for default

  • Add a .gitignore? → yes

  • Install dependencies with npm? → yes

Then install these:

npm install @openzeppelin/contracts@5.0.2 dotenv@16.4.5

Create a .env file in the root and add:

POLYGON_RPC_URL="YOUR_ALCHEMY_OR_INFURA_POLYGON_RPC_URL"
PRIVATE_KEY="YOUR_METAMASK_PRIVATE_KEY"
  • Swap in your Polygon RPC from Alchemy or Infura.

  • Use your MetaMask private key here—but keep it secret, seriously.

Update hardhat.config.js:

require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config();

module.exports = {
  solidity: "0.8.20",
  networks: {
    polygon: {
      url: process.env.POLYGON_RPC_URL || "",
      accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
    },
  },
};

Step 2: Building the Smart Contract

In the contracts folder, ditch the sample Lock.sol and make JPYCHandler.sol:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract JPYCHandler is Ownable {
    IERC20 public immutable jpycToken;

    event Deposit(address indexed user, uint256 amount);
    event Withdraw(address indexed owner, uint256 amount);

    address constant JPYC_POLYGON_ADDRESS = 0x431D5dfF03120AFA4bDf332c61A6e1766eF37BDB;

    constructor() Ownable(msg.sender) {
        jpycToken = IERC20(JPYC_POLYGON_ADDRESS);
    }

    /**
     * @dev Lets a user deposit JPYC into the contract.
     * @param _amount Amount of JPYC (with 18 decimals in mind).
     * Note: User needs to approve this contract first to spend their JPYC.
     */
    function depositJPYC(uint256 _amount) external {
        require(_amount > 0, "Amount must be greater than zero");
        uint256 allowance = jpycToken.allowance(msg.sender, address(this));
        require(allowance >= _amount, "Check the token allowance");
        bool success = jpycToken.transferFrom(msg.sender, address(this), _amount);
        require(success, "Transfer failed");
        emit Deposit(msg.sender, _amount);
    }

    /**
     * @dev Owner can pull out all JPYC from the contract.
     */
    function withdrawAllJPYC() external onlyOwner {
        uint256 balance = getContractBalance();
        require(balance > 0, "No JPYC to withdraw");
        jpycToken.transfer(owner(), balance);
        emit Withdraw(owner(), balance);
    }

    /**
     * @dev Check the contract's JPYC balance.
     * @return Balance in wei-like units.
     */
    function getContractBalance() public view returns (uint256) {
        return jpycToken.balanceOf(address(this));
    }
}

Step 3: Deploying and Interacting

Now let's get it on-chain and test it.

  1. Deployment Script

Tweak scripts/deploy.js:

const hre = require("hardhat");

async function main() {
  const jpycHandler = await hre.ethers.deployContract("JPYCHandler");
  await jpycHandler.waitForDeployment();
  console.log(`JPYCHandler deployed to: ${jpycHandler.target}`);
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Run it:

npx hardhat run scripts/deploy.js --network polygon

Grab the deployed address from the console.

  1. Interaction Script

Add scripts/interact.js:

const { ethers } = require("hardhat");

// CONFIG
const JPYC_HANDLER_ADDRESS = "YOUR_DEPLOYED_CONTRACT_ADDRESS";
const JPYC_TOKEN_ADDRESS = "0x431D5dfF03120AFA4bDf332c61A6e1766eF37BDB";
const DEPOSIT_AMOUNT = ethers.parseUnits("10", 18); // JPYC has 18 decimals

async function main() {
  const [signer] = await ethers.getSigners();
  console.log("Using wallet:", signer.address);

  const jpycHandler = await ethers.getContractAt("JPYCHandler", JPYC_HANDLER_ADDRESS, signer);
  const jpycToken = await ethers.getContractAt("IERC20", JPYC_TOKEN_ADDRESS, signer);

  console.log("\\nApproving spend...");
  const approveTx = await jpycToken.approve(JPYC_HANDLER_ADDRESS, DEPOSIT_AMOUNT);
  await approveTx.wait();
  console.log("Approved. Tx:", approveTx.hash);

  console.log("\\nDepositing...");
  const depositTx = await jpycHandler.depositJPYC(DEPOSIT_AMOUNT);
  await depositTx.wait();
  console.log("Deposited. Tx:", depositTx.hash);

  const contractBalance = await jpycHandler.getContractBalance();
  console.log("\\nCheck balance:", ethers.formatUnits(contractBalance, 18) + " JPYC");
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Swap in your contract address, then run:

npx hardhat run scripts/interact.js --network polygon

You should see the approval, deposit, and a balance of 10 JPYC.

Wrapping Up and Ideas for More

That's the basics for handling JPYC in a contract. The approve-then-transferFrom pattern is key for any ERC20 work—get comfy with it.

This simple setup can grow into cool stuff like:

  • An e-commerce payment system

  • Tipping for creators on blogs

  • Stable in-game money for Web3 games

With JPYC here, yen-focused Web3 projects have tons of potential. Grab this code, tweak it, and build something awesome.

I've put this guide together to give you a solid starting point for building with JPYC. Web3 development can sometimes throw a curveball, especially with different versions and environments.

If you follow the steps but still run into any trouble, or just have a question about the code, please feel free to reach out. I'd be happy to help clarify things. Happy building

Transform Your Vision into Reality with Web3

Take the next step into the future of blockchain. Partner with us today to build secure, scalable, and game-changing Web3 solutions that drive your business forward.

Transform Your Vision into Reality with Web3

Take the next step into the future of blockchain. Partner with us today to build secure, scalable, and game-changing Web3 solutions that drive your business forward.

Transform Your Vision into Reality with Web3

Take the next step into the future of blockchain. Partner with us today to build secure, scalable, and game-changing Web3 solutions that drive your business forward.