← Back to list

Making an on-chain Payment and Minting an NFT Receipt with Permissionless Price Oracles

dApp to accept ERC20 token payments and mint an NFT receipt with the USD value to the user.

Vansh Wassan · 2023-05-02 21:19 · 7 claps · 5.4 min read
#dapi #solidity #hard-hat #oracle #api3
Open on Medium ↗
Wiki topics: CRY · Crypto & Web3 FIN · Fintech & Banking

Making an on-chain Payment and Minting an NFT Receipt with Permissionless Price Oracles

dApp to accept ERC20 token payments and mint an NFT receipt with the USD value to the user.

Photo by Shubham Dhage on Unsplash

Photo by Shubham Dhage on Unsplash

Introduction

In my previous guide I explained how to request the latest price data on-chain. In this guide, I’m going to explain how to code a smart contract that accepts any ERC20 token as payment, and mints the payment receipt as an NFT that includes how much you paid to the contract in USD value at the time of payment. For this tutorial, I’m going to use a mock ERC20 token (Mock WETH) and set it to the ETH/USD dAPI for calculating the USD value for each payment.

*To know more about dAPIs, check out this previous guide*

API3 Market: Activating self-funded dAPIs

Developers can use the API3 Market to search for dAPIs, obtain dAPI proxy contract addresses, and monitor dAPIs. To access self-funded dAPIs, you can head to the API3 Market to fund and access a data feed so that a smart contract can utilize the value in a dApp.

https://market.api3.org/dapis

https://market.api3.org/dapis

Funding a feed

To use the self-funded dAPIs, the user is required to fund the respective dAPI sponsor wallet with gas to activate it. The amount of gas supplied will determine how long your dAPI will be available for use. If the dAPI runs out of gas, it can be funded again to keep it available for use.

If the feed is not already funded, users need to provide chain native collateral to the sponsor address of the required feed. This process happens through the API3 Market UI, where there is functionality to understand the estimated gas remaining and the time that the feed will be active.

Deploying the proxy contract

After ensuring the dAPIs are funded, you can deploy the proxy contract using the Market to access the dAPI.

If the proxy contract for a dAPI is already deployed, you can just start using it.

Getting Started

Before starting, make sure you have the following installed:

  • nodejs
  • yarn
  • git

We assume Javascript & Solidity basics are understood when reading this.

To get started, clone this repo:

$ git clone https://github.com/vanshwassan/dAPI-payments.git

To install the dependencies,

$ yarn

Coding the Contracts

There are 3 main contracts that we’ve used in this project.

  • Payments.sol
  • WETH.sol
  • WMATIC.sol

The Payments contract has many functions. Using the contract, any user can:

  • Send any ERC20 (Mock WETH, Mock WMATIC) payments to the contract and mint a Payment Receipt Token(PRT) NFT with the USD value of the assets at the time of payment.
  • Check the USD value of the assets using your NFT’s tokenId.

Similarly, the Owner (the address that deployed the contract) can:

  • Set the dAPI Proxies for the ERC20 tokens using the API3 Market.
  • Withdraw the total funds sent to the contract.

Payments.sol has all the functions required for collecting the ERC20 funds and calculating its USD value.

setDapiProxy() will be used to map and set the dAPI Proxy for the ERC20 tokens which the user will be required to pay. You can see a list of available dAPIs here.

function setDapiProxy(address token, address _proxy)
        public
        onlyOwner
    {
        tokenProxyMapping[token] = _proxy;
    }

getTokenPrice() function calls the dAPI and takes in the token address mapped to its dAPI Proxy obtained from the API3 Market and returns the latest price of the requested asset.

function getTokenPrice(address token) public view returns (uint256 tokenPriceUint256) {
        address proxy = tokenProxyMapping[token];
        int224 value;
        uint256 timestamp;
        (value, timestamp) = IProxy(proxy).read();
        uint224 tokenPriceUint224 = uint224(value);
        tokenPriceUint256 = tokenPriceUint224;
    }

The Payment() function takes in the address of the token and the total amount that the user wants to pay. It then calls the getTokenPrice() function to get the latest price and mints an NFT Receipt with a tokenId and the total USD value the user paid.

function Payment(address token, uint256 _tokenAmount) public returns(uint256) {
        require(_tokenAmount > 0, "amount cannot be 0");
        require(tokenIsAllowed(token), "token not allowed");
        if (tokenIsAllowed(token)) {
            uint256 tokenId = _tokenIdCounter.current();
            _tokenIdCounter.increment();
            uint256 decimals = ERC20(token).decimals();
            uint256 tokenPriceUint256 = getTokenPrice(token);
            uint256 _usdValue = (tokenPriceUint256 * _tokenAmount)/10**decimals;
            ERC20(token).transferFrom(msg.sender, address(this), _tokenAmount);
            _safeMint(msg.sender, tokenId);
            uint256 receipt = makeReceipt(tokenId, _usdValue);
            return receipt;
    }
    }

The checkReceipt() function is a view function that returns the total USD value of the ERC20 asset paid by the user by taking in the tokenId minted with the NFT Receipt at the time of payment.

function checkReceipt(uint256 tokenId) public view returns (uint256) {
        return (TokenIDtoPrice[tokenId]);
    }

The ownerWithdrawFunds() function that the owner of the contract can call to withdraw all the funds from the contract to his wallet.

function ownerWithdrawFunds() public onlyOwner {
        for (uint256 i = 0; i < allowedTokens.length; i++) {
            ERC20(allowedTokens[i]).transfer(msg.sender, ERC20(allowedTokens[i]).balanceOf(address(this)));
        }
    }

Here’s the full contract:

[embed]

You can find the repository with contract code here.

Deploying the Contracts

Before moving forward, enter your mnemonic in credentials.json to deploy and interact with the contracts.

$ cp credentials.example.json credentials.json

Now go to credentials.json and enter your credentials. As we’re testing this on Mumbai Testnet, you can just add your mnemonic under Mumbai. Make sure you fund your wallet with testnet MATIC before moving forward. You can request testnet MATIC here

Now compile the contracts:

$ yarn compile

To deploy the contracts and set the dAPI Proxies:

$ yarn deploy

This will deploy the Mock WETH and Mock WMATIC token contracts alongside the Payments Contract. It will also set the dAPI proxy contract addresses mapped to their respective ERC20 tokens. Head on to deploy/[3_deploy_Payments.js](https://github.com/vanshwassan/dAPI-payments/blob/master/deploy/3_deploy_Payments.js) If you want to make changes to the dAPI Proxy/use another ERC20 token.

Using the dApp

Import the Mock WETH contract address in your account’s Metamask. This token is just a mock token, a simple implementation of an openzeppelin ERC20 contract. As it’s a testnet, I’m going to call it WETH and map its value to the ETH/USD dAPI.

This token will be used to make payments to the contract.

Similarly, for the Mock WMATIC token, the value can be mapped to the MATIC/USD dAPI.

To use the scripts I made to interact with the contracts, head back to the Project and use the following commands.

  • Use the below command to send a payment to the contract. This will send 2.5 Mock WETH and 2.5 Mock WMATIC to the contract and will mint the Payments Receipt Tokens(PRT) to the user’s address as a digital proof of payment with the USD value of the token(s) at the time of payment. You can edit the amounts in scripts/[sendPayments.js](https://github.com/vanshwassan/dAPI-payments/blob/master/scripts/sendPayment.js)
$ yarn sendPayment
$ yarn checkReceipt
  • Use the below command to check the USD value of all the funds that the contract holds.
$ yarn checkContractBalance
  • Use the below command to withdraw all the funds stored in Contract. Only the owner of the contract can call this.
$ yarn withdraw

Conclusion

In this tutorial, you learned how to code a simple, usable dApp and how we can utilize dAPIs within a smart contract.

Check out the repository for this guide here.

Read more about dAPIs here.

Developers can get started with dAPIs now through the API3 Market. If you have any questions, you can join the API3 Discord server.


메타데이터
post_id
a7339f7b8c3e
slug
making-an-on-chain-payment-and-minting-an-nft-receipt-with-permissionless-price-oracles-a7339f7b8c3e
url
https://medium.com/@vanshwassan/making-an-on-chain-payment-and-minting-an-nft-receipt-with-permissionless-price-oracles-a7339f7b8c3e
canonical_url
https://medium.com/@vanshwassan/making-an-on-chain-payment-and-minting-an-nft-receipt-with-permissionless-price-oracles-a7339f7b8c3e
author_url
https://medium.com/@vanshwassan
status
ok
fetched_at
2026-07-25 20:01:13