# Overview

Doki Doki is a web3 platform that enables the creation of provably-fair NFT Gacha machines on the blockchain, which simplifies the process of launching a randomized NFT sale for projects, artists, and collectors.

Gacha machines are highly customizable smart contracts that dispense randomized NFTs in exchange for tokens.

Popular use cases include:

* fairly distributing in-game items
* launching a collectible NFT series
* reselling a batch of NFTs

Doki Doki is permissionless and non-custodial, so you can use Gacha however you like.

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2FltmGR7lVneSAEtMVuGfk%2Fimage.png?alt=media\&token=e280fdaf-0d12-4668-b9e2-9c9f053ebb54)

### Core Components

* **The Gacha Protocol** - a set of smart contracts that enable the creation of on-chain Gachas
* [**dokidoki.com**](https://dokidoki.com/) - The official interface for interacting with the Gacha Protocol, which includes The Gacha Factory, The Gacha Marketplace, Collection Manager, and more

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2FVeVZr0YKvwI2ldeBqu1q%2Fimage.png?alt=media\&token=24afaf39-b836-4d0e-8e9b-76a85d3e5ec4)

Doki Doki is initially available on Polygon, with plans to go multi-chain this year.


# Key features

### Compatible with all NFT types and collections

Gacha machines can be loaded with ERC-721 and ERC-1155 NFTs from any collection, regardless of where they were originally minted. Moreover you can mix and match NFTs from multiple different collections in a single machine. The only requirement is that the NFT and the Gacha exist on the same chain, e.g. NFTs on Polygon can be loaded into Gacha on Polygon.

For example, you can mint NFTs on Opensea and load them into your Gacha for randomized distribution. This also means that you earn secondary market royalties if you have it setup for your collection.

### On-chain and Non-custodial

Gacha machines are on-chain smart owned and contracts controlled by you. This means that the protocol is never in possession of your NFTs or funds. Tokens earned by your Gacha are automatically streamed to your wallet in real-time. You can also remove NFTs from Gachas that you own.

### Composable

As public smart contracts, Gacha machines are fully composable. By integrating the Gacha protocol into games, money, apps, and websites, developers can save both time and resources required to build custom solutions. Check out our [developer resources](https://docs.dokidoki.com/resources/for-developers) to get started. Reach out to us on [Discord](https://discord.com/invite/K32BeEY) if you have any questions.

### Provably fair

The GACHA protocol uses ChainlinkVRF, the industry standard for on-chain RNG (Random Number Generation). This ensures that the prize distribution system is unbiased and cannot be gamed. Gacha players and collectors can audit the RNG results in their play-history at anytime. [Learn more about ChainlinkVRF.](https://docs.chain.link/docs/chainlink-vrf/)

### Payout Splits

Gacha machines can be configured to split tokens earned (up to 10 different wallet addresses)

### Custom Token Support

By default, Gachapons can be configured to accept ETH, USDC, DAI for paymen&#x74;**.** If you are a project representative, contact us on [Discord](https://discord.com/invite/K32BeEY) to get your ERC-20 token whitelisted. Once approved, you will be able to create Gacha machines that accept your project token.

### Highly Customizable

Below table lists the customizable attributes for each Gacha machine. Some are fixed at deployment while others can be modified afterwards. Key attributes are on-chain, while the visual style options are stored off-chain. Only the owner (creator) of the Gacha has the ability to modify these settings.

<table><thead><tr><th>Attribute</th><th width="150">Example</th><th width="150">On-chain? (Y/N)</th><th width="178">Can be modified post deployment? (Y/N)</th></tr></thead><tbody><tr><td>Blockchain Network</td><td>Polygon, more chains coming soon</td><td>Yes</td><td>No</td></tr><tr><td>Accepted Token</td><td>USDC, ETH, DAI</td><td>Yes</td><td>No</td></tr><tr><td>Price per play</td><td>0.01, 0.5,1000</td><td>Yes</td><td>Yes</td></tr><tr><td>Name</td><td>"My Gacha"</td><td>Yes</td><td>Yes</td></tr><tr><td>Category</td><td>Gaming, Art &#x26; Collectibles</td><td>Yes</td><td>Yes</td></tr><tr><td>Gacha Description</td><td>"A Gacha with some cool NFTs!"</td><td>Yes</td><td>Yes</td></tr><tr><td>Payout Address(s) and % split</td><td>0x12...232 50%, 0x23..523 50%</td><td>Yes</td><td>Yes</td></tr><tr><td>Machine Design</td><td>Coming Soon</td><td>Coming Soon</td><td>Coming Soon</td></tr><tr><td>Gacha Ball Design</td><td>Coming Soon</td><td>Coming Soon</td><td>Coming Soon</td></tr><tr><td>BG Image</td><td>Coming Soon</td><td>Coming Soon</td><td>Coming Soon</td></tr><tr><td>Color Theme</td><td>Coming Soon</td><td>Coming Soon</td><td>Coming Soon</td></tr><tr><td>Soundtrack</td><td>Coming Soon</td><td>Coming Soon</td><td>Coming Soon</td></tr></tbody></table>


# Protocol Fees

### Set-up Fee

There is a one-time setup fee for deploying a Gachapon. On Polygon, the cost is initially set to 20 MATIC tokens (\~$40). This fee is used to purchase the LINK tokens required to cover the cost of ChainlinkVRF.

### Protocol Fee

For Gachapon plays we have set an initial flat fee of 5%, automatically deducted from the tokens earned. We don’t want fees to hinder adoption and thus have decided on a fee much lower than most other NFT platforms, making the Gacha protocol a viable option even for secondary market sales.

### Play-count limit

As a safety measure, the protocol allocates each Gachapon enough LINK for 10,000 plays (10k NFTs distributed). If you plan on deploying a Gachapon that requires more than 10,000 plays, reach out to us for an increase in the allocation. In a future update, Gacha owners will be able to increase the play count allocation using the Gacha Factory.


# FAQ

#### **Q: Which Blockchain network is the Gacha Protocol available on?** <a href="#bb9d" id="bb9d"></a>

A: Initially available on Polygon, we are working on expansion to other EVM compatible chains such as BSC, Fantom, Avalanche, and others.

#### Q: Why can’t you add support for Ethereum L1? <a href="#id-6589" id="id-6589"></a>

A: The cost of gas and ChainlinkVRF on Ethereum L1 makes our Gacha machines too expensive to use. For the same reason, many NFT projects, especially gaming ones are choosing to deploy on cheaper and more scalable L2 networks. That being said if costs decrease to acceptable levels, we have no problems making The Gacha Protocol available on L1.

#### Q: What’s the difference between Standard and Curated machines? <a href="#id-8c89" id="id-8c89"></a>

A: Occasionally, the team may collaborate directly with artists or projects to launch a curated machine. This does not mean that curated machines are better than standard machines in terms of quality. With the launch of the permissionless GACHA protocol, expect less curated machines as we focus more on protocol growth & expansion.

#### Q: Can I load additional NFTs into my Gacha machine later? <a href="#id-1505" id="id-1505"></a>

A: Yes, simply lock your machine and you will be able to load more NFTs. Unlock your machine afterwards to let others people play again.

#### Q: Is it possible to remove NFTs from my Gacha machine? <a href="#ded2" id="ded2"></a>

A: Yes, although it will permanently lock the machine. Please refer to the [instructions here](https://docs.dokidoki.com/how-to-remove-nfts-from-gachapon).

#### Q: Will the 5% fee on my Gacha machine ever change? <a href="#id-3ae5" id="id-3ae5"></a>

A: No, the 5% fee is fixed during deployment. Any future modification to the protocol fee will only impact new Gachapons deployed after the change has taken effect.

#### Q: What does locking and unlocking my Gachapon do? <a href="#id-3b3f" id="id-3b3f"></a>

A: Locked = Maintenance mode. No one can play your Gachapon but you can safely load NFTs into it. Unlocked = Active mode, anyone can play your Gachapon and win NFTs. Remember to set a price per play, before you unlock your Gachapon.

#### **Q: I played a Gacha, but the balls didn't appear, where are my NFTs?** <a href="#bb9d" id="bb9d"></a>

A: Your NFTs are safe. You can claim them by visiting the [play-history tab in your collection page](https://dokidoki.com/collection/history)


# How to create NFT Gacha

This tutorial will guide you through the process of launching your own Gacha, broken down into three simple steps 1) Deploy 2) Configure 3) Load NFTs

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2F30hWPI6fbL2AnoQOnvTt%2FGachaFactory.png?alt=media\&token=545112d0-d812-4a69-b4e4-aa24ad5a66b2)

## 1. Deploying a new Gacha

To deploy a new Gacha, head over to the[ Gacha Factory](https://dokidoki.com/factory) and click "Deploy New Standard Gachapon". Select the network you wish to deploy on, the accepted token, and input a name for your Gacha (don't worry, you can change it after). Press deploy, confirm the dialog in your Metmask, and wait for the transaction to process. Done!

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2FTJNV9z9Zyu2sDNY6KEGO%2Fimage.png?alt=media\&token=fd913786-d07f-45f0-8569-5d4646da2090)

## 2. Configuring your Gacha

Click on your newly deployed Gacha and you'll be able to modify its settings which include:

* Machine Name - Self Explanatory
* Price per Play - The cost of getting one random NFT from your Gacha
* Category - Art & Collectibles, Gaming, or leave it uncategorized
* Description - A brief description which will appear on your Gacha page
* Payout addresses - Gachas can automatically split tokens earned to up to 10 different wallet addresses. Percentage share of the addresses must add up to 100%

Each of the above attributes are stored on-chain, so you'll need to click the "Apply" button and confirm a Metamask transaction for changes to take effect. As the owner of the Gacha, you can change these settings at anytime.

{% hint style="info" %}
The ability to fully customize the look & feel of your Gacha is a feature we're currently working on.
{% endhint %}

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2FqWIsr74hyhnynoHxQNH5%2Fimage.png?alt=media\&token=8db70f81-6d9c-4302-8bd0-f6cbf284d09f)

## 3. Loading NFTs into your Gacha

{% hint style="info" %}
**Important Note:** For NFTs minted using the [Opensea public minter](https://opensea.io/asset/create), you must **Freeze its metadata** before loading into your Gacha for the media to display correctly. Please refer to [the instructions posted here.](/tutorials/how-to-freeze-metadata-for-nfts-minted-on-opensea)
{% endhint %}

### **For** NFTs created using the Doki Doki Minter

Navigate to the NFT Loader tab in the Gacha Factory. You should be able to see all NFTs you created using the Doki Doki official minter. Select your Machine at the top left, and click on the NFT you wish to load. Input the quantity, confirm the transaction in Metamask and you are done!

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2FdOVggcsnCbeu3CMjmk6v%2Fimage.png?alt=media\&token=0faf62a0-5108-4f81-8d5a-80aeaa4141df)

### **For all other NFTs (Not minted using the Doki Doki Minter)**

Click the purple "Load NFTs from Custom Collection" button. A popup will appear with the instructions.&#x20;

The easiest and recommended way to load is by pasting the full Opensea link to the NFT, inputting the quantity, and confirming the transfer.

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2FXhB7YYJOr35JpX5L0RCi%2Fimage.png?alt=media\&token=557c373c-9886-405a-96c5-0d7a9a6a63d6)

If for some reason the above did not work, you can also load the NFT by transferring it directly to the Gacha contract address. You can do this using Opensea's transfer function.

![Opensea Transfer Button](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2Fwos9Ug4AksvlH8W8OZU2%2Fostransfer.png?alt=media\&token=2c86bd2d-e997-4f5d-9b9e-b70edeac62e3)

Once the transfer is confirmed, check your Gacha play page to verify if the NFT was successfully loaded.

{% hint style="info" %}
You can load up to 800 copies of a NFT into your Gacha in one transaction, exceeding this limit may cause the action to be reverted. Always doublecheck the address of the Gacha before sending. Sending to incorrect address may result in the loss of your NFT.
{% endhint %}

## Unlocking or Locking your Gacha

Once you're done configuring your Gacha and loading NFTs, unlock your machine to allow others to play. The unlock button can be found in both the Gacha Factory and NFT Loader tabs. In summary:

* **Locked** = Maintenance mode. No one can play your Gacha, but you can safely load NFTs into it while your Gacha is locked.
* **Unlocked** = Active mode. Others can play your Gacha to win NFTs. You cannot load new NFTs while your Gacha is unlocked.


# How to remove NFTs from Gacha

{% hint style="info" %}
Warning: Removing a NFT will lock the Gachapon PERMANENTLY, rendering it unplayable
{% endhint %}

At this time, removing NFTs from a Gachapon requires manual contract interaction. Follow the instructions below.

1. Visit the Polygonscan page of the Gachapon contract. You can find the address of a particular Gachapon in the URL of its webpage. For example,&#x20;

<img src="https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2Fw3cHpXOqHXhKLbUuA1Ll%2Fimage.png?alt=media&amp;token=f897a118-6e7e-45a9-b207-2391556468d0" alt="" data-size="line">

Each Gachapon contract address is unique so make sure you have the one that corresponds to yours.

Search the address on [Polygonscan. ](https://polygonscan.com/)You should now arrive at the contract page.

&#x20; 2\.  Navigate to contract -> write contract and connect using the same wallet which owns the Gachapon machine

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2F9YxDmikzRCtuvsX1SX1e%2Fimage.png?alt=media\&token=37b665c7-a9d7-46e8-b2ec-1ba2ee5ff5f8)

3\. Scroll down to method 8. Remove NFT. Here you will need to input The NFT’s collection address, ID, and quantity you wish to remove. You can find the NFTID and collection address on the expanded details on Doki Doki or Opensea. See below examples

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2F6NMyxmEc3XuzTc2tgOYY%2Fimage.png?alt=media\&token=f4987ed4-3061-4f27-9fb3-4c53c750cac3)

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2F8aKDGxLUqeWVZ6x5SnxH%2Fimage.png?alt=media\&token=3c5fc83b-31bd-4f46-8a03-8f01ccc2af62)

The above are just examples. Make sure you have the right ID and address for the NFTs you want to remove.&#x20;

4\. Now copy the NFTID and collection address and go back to the Polygonscan page. Paste them into the corresponding input box along with the quantity you wish to remove and click write. Once the transaction confirms, that particular NFT is now removed from the machine and should be transferred back into your wallet.

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2FDEGbVT13J6EifE0hO6pC%2Fimage.png?alt=media\&token=f7ec6139-c1d8-4554-8cef-ef0c9653aa02)

If your wallet displays contract execution error, usually that means there is a problem with one or more of your inputs. If you need help, reach out to us on Discord for support.

If you wish to remove all NFTs, repeat steps 3-4 until all NFTs have been removed from the machine.


# How to freeze metadata for NFTs minted on Opensea

How to make NFTs minted using Opensea public minter compatible with Gacha

### Context

For NFTs minted using the [Opensea public minter](https://opensea.io/asset/create), you must **Freeze its metadata** before loading into your Gacha for the media to display correctly. Freezing the metadata basically means uploading the content to IPFS, a storage solution that is permanently available and unchangeable. To do this, please refer to the below instructions:

### Instructions

1. Navigate to the Opensea page of your NFT and click the "Edit" button at the top

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2F3augr9q8JAI2zu7YeiHj%2Ffreezemetadata1.png?alt=media\&token=bcf7b132-dde3-4a93-b18f-43229a356bce)

2\. On the edit page, scroll down to the feature and toggle on "Freeze metadata" then click the "Freeze" button that appears

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2FdR1TeCSWklULCxEJYlBs%2Ffreeze2.png?alt=media\&token=d4de0489-c16a-4348-adf3-5949ddd5f3a0)

3\. Confirm the dialog and you're done! Repeat steps 1-3 for each NFT that you wish to load into your Gacha.

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2FvVWFaEGZcaCLC9GJtVte%2Fimage.png?alt=media\&token=16cacde7-217c-4f7f-82a0-2f5f64130f02)

With the metadata frozen, the NFTs can now be loaded into your Gacha. You can verify that a particular NFT's metadata is frozen by its snowflake icon at the top left.&#x20;

{% hint style="info" %}
The snowflake icon is only applicable to NFTs minted using Opensea's public minter. Most custom collections with a fixed URI should be natively compatible with our Gacha so you don't need to freeze the metadata. Contact us on Discord if you are unsure.
{% endhint %}

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2Fs7TfYnJE3Sm8STrD3iUS%2Fgachatoken3.png?alt=media\&token=43b02ff9-8dfc-444a-8d20-16e2dc540ded)


# How to add special rules to your Gacha using .CSV file

You can add special rules for specific wallet addresses such as **allowlists** to restrict access, **discounted plays,** and **free plays** by uploading a .CSV file with three columns:

* **address** = wallet address of users you wish to apply rule to&#x20;
* **playcount** = number of times each address can play&#x20;
* **price** = the price user can play for:
  * blank = current set price&#x20;
  * 0 = free

The easiest way to create a .CSV file is to do so in a spreadsheet such as google sheets or Excel.

**Example:**

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2F0h6jMCpSRxMv4OeiDCNr%2Fimage.png?alt=media\&token=eaeb3444-693b-4e7a-9b44-c12fd460f9a0)

Once you have filled out your spreadsheet, you can export it as a .CSV file as shown below:

**Google Sheets:** File -> Download -> Comma Separated Values (.CSV)

<figure><img src="https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2FBOaN1Lv2aaa4T8KicQUR%2Fimage.png?alt=media&amp;token=1ec239d5-61f1-4298-a193-f09300c10c60" alt=""><figcaption></figcaption></figure>

&#x20;**Microsoft Excel:** File -> Export -> Change File Type -> CSV (Comma Delimited)

<figure><img src="https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_aL7C05HrAJjsqdz--%2Fuploads%2FV1SSIRFmDtIEBLiiTvON%2Fimage.png?alt=media&amp;token=c7dbd358-b523-46e7-8837-3857e1947937" alt=""><figcaption></figcaption></figure>


# For developers

You want to build on top of our gachapons? This is the right place to start!

{% hint style="info" %}
This guide is under construction, don't hesitate to ping `Kirienzo` on Discord or Telegram if you have some questions, or would like to suggest some improvements for this guide
{% endhint %}

## Note on Old vs New Gachapons

All gachapons that were created before the 2021/08/19 are considered deprecated.

Essentially, all gachapons that were not created by our factory and are not available in the [gachapon subgraph](https://thegraph.com/legacy-explorer/subgraph/kirienzoeth/dokidoki-gachapons) (you can use [this subgraph ](https://thegraph.com/legacy-explorer/subgraph/kirienzoeth/dokidoki-gachapons)for mumbai, Polygon's testnet) will not be covered by this guide.

## The Gachapon Control Tower

The factory that creates the Gachapons on demand is called the Control Tower. It's available on the available networks:

* Polygon mainnet: 0xC15e5C4fe1730562dC866ad9385F652f1D4C43fd
* Polygon mumbai testnet: 0xf37988C2E343cB2698c31ECbb57d8C86FAb6Fc5D

## The Gachapon Interface

All gachapons created by our platform will implement the `IGachapon.sol` interface, meaning that it doesn't matter which ones you want to communicate with, they will all have the same entry-points. You can find the `Gachapon.sol` file and its ABI here: [Solidity Interfaces and ABIs](/resources/for-developers/solidity-interfaces-and-abis)

### How to add NFTs to a gachapon

To add NFTs to a gachapon, you simply need to send them to the smart contract either by calling `safeTransferFrom` or `safeBatchTransferFrom` on your NFT collection smart contract. Simply put, you can use OpenSea's interface to just send the NFTs to the address of the gachapon, they will then be added to the prize pool.

#### Important notes:

Only ERC1155 NFTs are supported as of today. Trying to send an ERC721 NFT to a gachapon will result in a failed transaction.

You need to be the owner of the gachapon to add NFTs to it, verify that you are the owner by calling the `owner` method of the smart contract. All gachapons implement the `Ownable.sol` smart contract from OpenZeppelin.

### How to play with a gachapon

The `play(uint8 _times)` is the method you're looking for.\
Because of the gas limitations the VRF callback is imposing, the `_times` argument needs to be between 1 and 10, any other value will throw an error and revert the transaction.

Here is an example using web3js and Typescript:

```typescript
import Web3 from 'web3';
import { Contract } from "web3-eth-contract";
import { PromiEvent } from 'web3-core';

playGachapon(gachaponAddress: string, userAddress: string, times: number): PromiEvent<any> {
  // Inject the wallet of the user
  const web3 = new Web3(window.ethereum)
  // Create the contract
  const contract = new web3.eth.Contract(ABI, gachaponAddress);
  // Send the transaction
  return contract.methods.play(times).send({ from: userAddress });
}
```

The `playGachapon` method will trigger the wallet of your user and, if accepted, will send the TX, and will start what we will call a "round"

### Track the status of a round

If the transaction is successful, the receipt will contain an `RoundStarted` event, this event gives you the `_requestId` of the round. This ID is the one returned by the VRF oracle so it can call our smart contract back with a random number for a particular round, this ID is unique even across several gachapons.

You can use this request ID to track the rounds status, and do something when the oracle answered, on our side, we use an RPC endpoint that supports websocket connections (usually starts with `wss://` instead of the usual `https://`) to listen to a `RoundCompleted` event with the same request ID:

```typescript
const rpcUrl = '<Your RPC URL that supports websocket>';
const web3 = new Web3(rpcUrl);
const contract = new web3.eth.Contract(ABI, gachaponAddress);
// Listen to the next RoundCompleted event with this requestId
contract.once('RoundCompleted', {
  filter: {_requestId: requestId},
  fromBlock: 'latest'
}, (error: Error, event: EventData) => {
  if (error) {
    throw error;
  }
  
  // Extract the prizes indexes from the event
  const nftIndexes: number[] = (event.returnValues._prizes as string[]).map((prize) => +prize);
  const timesPlayed: number = +event.returnValues._times;
  
  // By default, a round contains an array of 10 zeros, so you need to 
  // take only the prizes which have an index lower than the `_times` value
  const prizes = nftIndexes.splice(0, timesPlayed);
  // This array will contain the index at which the prizes are in the gachapon, 
  // you can call `getNft()` on the contract to get the details of a particular NFT
  console.log(prizes);
});
```

{% hint style="info" %}
On average, the oracle will take between 10 and 30 seconds to callback the smart contract, so don't panic if it doesn't answer instantly.
{% endhint %}


# Supported token URI values

The protocols and IPFS link formats supported by our API.

## IPFS

If the metadata of your NFT is hosted on IPFS, great! We should be able to get it without any issue, but as there are several ways to write a link to an IPFS resource, we want to make sure yours is included. Our API supports both CID v0 and v1.

The `uri` method of your NFT collection should return:

* ipfs\://{your CID}
* <https://ipfs.io/ipfs/{your> CID}
* /ipfs/{your CID}
* A valid CID directly
* A valid CID path directly

## HTTPS link

If the `uri` method of your NFT collection returns a link to an API, it should be something we can call right away. If you need the link to be concatenated with the token ID or any other kind of processing, contact us.


# Supported metadata structures

How to be sure that your NFT will show up correctly on dokidoki.com

## OpenSea metadata standards

If your NFTs use the [OpenSea metadata standards](https://docs.opensea.io/docs/metadata-standards#metadata-structure), you are already good to go.

## Other structures

If you don't follow the above standards, worry not, you might still be using the following structures:

| Type of data                     | Key in your metadata               |                                                                                                                                                                                                                                                                                                                         |
| -------------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Image (PNG, JPEG, JPG, GIF only) | <p>image</p><p>image\_url</p>      | The image that represents your NFT, we will use that key to generate the preview of your NFT. Any other file format than an image will be rejected.                                                                                                                                                                     |
| Video (MP4 Only)                 | animation\_url                     | Video of your NFT, usually the full resolution display of it. **If your structure contains this key, it MUST also contains the preview in the "image" category.**                                                                                                                                                       |
| name                             | name                               | The name of your NFT that will be displayed on our site.                                                                                                                                                                                                                                                                |
| description                      | description                        | The description of your NFT                                                                                                                                                                                                                                                                                             |
| Attributes                       | <p>attributes</p><p>properties</p> | <p>An array of properties with the following format:</p><p><code>\[</code></p><p>  <code>{</code></p><p>     <code>"trait\_type": "name of property",</code></p><p>     <code>"value": "value of property"</code></p><p>  <code>},</code></p><p>  <code>{...},</code></p><p>  <code>...</code></p><p><code>]</code></p> |

Any other data will not be taken into account.

## My NFTs don't follow any of those structures :(

Worry not! You can still use our gachapons, but the NFTs might not be displayed properly (No preview, no name...).

If you want them to be supported, you can just contact us beforehand on Discord or Telegram. We will do our best so they can be integrated ASAP.


# Solidity Interfaces and ABIs

## IGachapon

### Interface

{% code title="IGachapon.sol" %}

```javascript
pragma solidity ^0.8.6;

/// @title Interface implemented by all gachapons created by the GachaponControlTower
/// @author KirienzoEth for DokiDoki
interface IGachapon {
  /// @dev Undefined = Round doesn't exist, Pending = waiting for oracle response, Unclaimed = oracle answered, Completed = Prizes were withdrawn
  enum RoundStatus { Undefined, Pending, Unclaimed, Completed, Cancelled }
  
  struct Round {
    bytes32 id; // request id
    address player; // address of player
    RoundStatus status; // status of the round
    uint8 times; // how many times of this round
    uint256[10] prizes; // Prizes obtained in this round
  }
  
  struct Nft {
    address collection; // adress of the contract of the collection
    uint256 id; // token ID of the nft
    uint256 amount; // winnable amount left in the gachapon
  }

  /// @notice Get the state of a round
  function getRound(bytes32 _roundId) external returns (Round memory);
  /// @notice Get the token address of the currency used by this gachapon
  function currency() external returns (address);
  /// @notice Return the number of prizes that are still available
  function getRemaningPrizesAmount() external returns(uint256);
  /// @notice Get the nft at index `_index`
  function getNft(uint256 _index) external returns(Nft memory);
  /// @notice The number of different nfts in the gachapon
  /// @dev Use this to query `getNft` without going out of bounds, latest index is `nftsAmount() - 1`
  function nftsAmount() external view returns(uint256);
  
  /// @notice Play the gachapon `_times` times
  function play(uint8 _times) external;
  /// @notice Claim the prizes won in a round
  function claimPrizes(bytes32 _roundId) external;
  /// @notice Transferring ownership also change the artist's address
  function transferOwnership(address _newOwner) external;

  /// @dev Player paid and oracle was contacted, refer to getRound(_requestId) to check if the prizes were distributed or not
  event RoundStarted(bytes32 indexed _requestId, address indexed _player, uint8 _times);
  /// @dev Oracle answered and the drawn prizes were stored, numbers in _prizes are indexes of the variable 'nfts'
  event RoundCompleted(bytes32 indexed _requestId, address indexed _player, uint8 _times, uint256[10] _prizes);
  /// @dev Oracle didn't answer and an operator re-created a round for this player
  event RoundCancelled(bytes32 _requestId);
  /// @dev Stored prizes were sent to the user
  event PrizesClaimed(bytes32 indexed _requestId);
}
```

{% endcode %}

### ABI

```javascript
[
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "bytes32",
          "name": "_requestId",
          "type": "bytes32"
        }
      ],
      "name": "PrizesClaimed",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "bytes32",
          "name": "_requestId",
          "type": "bytes32"
        }
      ],
      "name": "RoundCancelled",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "bytes32",
          "name": "_requestId",
          "type": "bytes32"
        },
        {
          "indexed": true,
          "internalType": "address",
          "name": "_player",
          "type": "address"
        },
        {
          "indexed": false,
          "internalType": "uint8",
          "name": "_times",
          "type": "uint8"
        },
        {
          "indexed": false,
          "internalType": "uint256[10]",
          "name": "_prizes",
          "type": "uint256[10]"
        }
      ],
      "name": "RoundCompleted",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "bytes32",
          "name": "_requestId",
          "type": "bytes32"
        },
        {
          "indexed": true,
          "internalType": "address",
          "name": "_player",
          "type": "address"
        },
        {
          "indexed": false,
          "internalType": "uint8",
          "name": "_times",
          "type": "uint8"
        }
      ],
      "name": "RoundStarted",
      "type": "event"
    },
    {
      "inputs": [
        {
          "internalType": "bytes32",
          "name": "_roundId",
          "type": "bytes32"
        }
      ],
      "name": "getRound",
      "outputs": [
        {
          "components": [
            {
              "internalType": "bytes32",
              "name": "id",
              "type": "bytes32"
            },
            {
              "internalType": "address",
              "name": "player",
              "type": "address"
            },
            {
              "internalType": "enum IGachapon.RoundStatus",
              "name": "status",
              "type": "uint8"
            },
            {
              "internalType": "uint8",
              "name": "times",
              "type": "uint8"
            },
            {
              "internalType": "uint256[10]",
              "name": "prizes",
              "type": "uint256[10]"
            }
          ],
          "internalType": "struct IGachapon.Round",
          "name": "",
          "type": "tuple"
        }
      ],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "currency",
      "outputs": [
        {
          "internalType": "address",
          "name": "",
          "type": "address"
        }
      ],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "uint8",
          "name": "_times",
          "type": "uint8"
        }
      ],
      "name": "play",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "bytes32",
          "name": "_roundId",
          "type": "bytes32"
        }
      ],
      "name": "claimPrizes",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_newOwner",
          "type": "address"
        }
      ],
      "name": "transferOwnership",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "_index",
          "type": "uint256"
        }
      ],
      "name": "getNft",
      "outputs": [
        {
          "components": [
            {
              "internalType": "address",
              "name": "collection",
              "type": "address"
            },
            {
              "internalType": "uint256",
              "name": "id",
              "type": "uint256"
            },
            {
              "internalType": "uint256",
              "name": "amount",
              "type": "uint256"
            }
          ],
          "internalType": "struct IGachapon.Nft",
          "name": "",
          "type": "tuple"
        }
      ],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getRemaningPrizesAmount",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "nftsAmount",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
  ]
```

###


# Brand & Media Kit

### Logo with text

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M_aL7C05HrAJjsqdz--%2F-MdQpjBWRkZ67VS_BNhV%2F-MdQrTVXcX4RXIMP-bpm%2Fdokidoki_logo_white_text_filled_bg.png?alt=media\&token=a2324c1b-8a79-4c15-a125-ce6c22c2a307)

{% file src="/files/-MdQrgrlKC4H1aPowxOX" %}
Logo with text filled BG SVG&#x20;
{% endfile %}

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M_aL7C05HrAJjsqdz--%2F-MdQpjBWRkZ67VS_BNhV%2F-MdQrxP69LDM94408KiY%2Fdokidoki_logo_white_text_transp_bg.png?alt=media\&token=547714a0-878c-470d-aa3c-17044f650280)

{% file src="/files/-MdQs6T9KZ00ewQ59d96" %}
Logo with text transparent BG SVG
{% endfile %}

### Logo text only

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M_aL7C05HrAJjsqdz--%2F-MdQpjBWRkZ67VS_BNhV%2F-MdQu26rXeWMQtC5390C%2Fdokidoki_textonly_pink_bg.png?alt=media\&token=fc91ad63-ff3d-4b4e-81bc-53bf41976616)

{% file src="/files/-MdQuI-hxs6H0jrcpXAY" %}
Logo text only filled BG
{% endfile %}

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M_aL7C05HrAJjsqdz--%2F-MdQpjBWRkZ67VS_BNhV%2F-MdQutBLvWvJADNJpQgh%2Fdokidoki_textonly_transp_bg.png?alt=media\&token=0743ce73-3894-44b4-9427-1b0850078ea1)

{% file src="/files/-MdQv02zAI3Xs-w08R\_s" %}
Logo text only transparent BG
{% endfile %}


# Disclaimer

Doki Doki is a blockchain-based platform that anyone can use to mint NFTs (Non-fungible tokens), deploy Gacha machines, spend tokens to play Gacha machines to obtain NFTs

Doki Doki is non-custodial and only provides an interface for users to interact directly with smart contracts (Gacha machines, Staking pools) and assets (Tokens, NFTs) which reside on public blockchain networks. Users of Doki Doki should read the documentation carefully be aware of risks which include but are not limited to: losses due to decrease in value of tokens, NFTs or potential smart contract bugs.

Components of the platform such as Gacha machines contain elements of “chance” and thus may potentially be classified as “gambling”. Users are responsible for reviewing and understanding the legal status of Doki Doki in their local jurisdiction, before interacting with the platform.<br>


# Customizations

How to personalize your Gacha machine


# Chainbinders

A first-of-its-kind deflationary set of NFTs backed by real value in $ETH

![The gathering storm is here!](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M_aL7C05HrAJjsqdz--%2F-MdRyr64-N-p7AKk1qQ-%2F-MdS-LQ_Qx66a1GYsJ2o%2Fimage.png?alt=media\&token=e09b18ed-aee6-4e84-97a9-7b20561446b0)

## Overview

The world of Chainbinders takes place in the near future — a large solar flare brings humanity to the brink of extinction, the ensuing chaos bringing about the destabilization of society. The solar flare kills most of the humans it comes in direct contact with — but a few destined children around the world survive the event, gaining mysterious powers. The focus of the story revolves around fifteen of these children, who come to be known as Chainbinders — named such for their unique and singular appearance. Chainbinders seek their Collectors, individuals with the ability to restore a Chainbinders to their full strength.

{% embed url="<https://youtu.be/-_7qz7_o_2I>" %}

Chainbinders is an original Doki Doki IP product directed by [Roy Blackstone](https://twitter.com/fillbeforeshill) which features:

* A special [Gacha machine](https://dokidoki.com/playgacha/3/0xeFd7b1DC887b79568A59c39e6d35B6F2F2D18f02) with over 110 original NFTs from the Chainbinders universe
* [$BND](https://www.coingecko.com/en/coins/doki-doki-chainbinders), a custom token which is required to play the Chainbinders Gacha
* A [reserve pool](https://polygonscan.com/address/0x36be28fd7ba9a610b1ff3866f2dc9b7807c6ee4e) of ETH which backs the value of the NFTs
* A [dedicated collection page](https://chainbinders.degacha.com/my-collection) where the NFTs can be burned a.k.a "unbound" for ETH
  * Rare NFTs are worth more ETH than common NFTs
  * Unbinding increases both the scarcity and ETH value of remaining NFTs in the set
* A[ lore page ](https://chainbinders.degacha.com/chainbinders)with short stories for each of the 15 main characters
* A [gallery page](https://chainbinders.degacha.com/universe/gallery) showcasing Chainbinders OST, trailers, and wallpapers

*Like all Doki NFTs, Chainbinder NFTs were minted natively on Ethereum and bridged to Polygon. Using the* [*Doki Doki collection page,*](https://dokidoki.com/collection) *you can transfer the NFTs back to Ethereum L1 for safe-keeping.*

### BND Token

In the beginning, 1,255,206 $BND tokens were minted along with 125,520 NFTs. The Gacha costs 10 $BND per play and the 100% of the tokens are burned, making $BND a hyper-deflationary. The circulating supply of $BND will reach zero once there are no NFTs remaining in the machine. No additional $BND can ever be minted, however you can still experience Chainbinders by purchasing $BND on the open market and playing the Gacha!

* [Purchase BND on Comethswap L2](https://charts.cometh.io/pair/0xb3bed360bec6b70d2604993ab686b3e6fd3dc230)
* [Purchase BND on Uniswap L1](https://v2.info.uniswap.org/token/0X84CD68C3E470ECEE4B8B6212EFCB8C6BCB38DA1D)

As of August 2021, over 700k BND tokens (\~ 55% of the supply) have been locked in the machine contract and removed from circulation. [Source](https://polygonscan.com/address/0xeFd7b1DC887b79568A59c39e6d35B6F2F2D18f02)

## **Unbinding**

Unbinding is the process in which a player sacrifices their Chainbinders NFT along with a small amount of $AZUKI tokens in exchange for ETH. 10% of the extracted value loops back into reserve pool which increases the redeemable ETH value of the other NFTs, rewarding long-term collectors.&#x20;

![](https://3490508129-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M_aL7C05HrAJjsqdz--%2F-MdVOd72bBlSAIIPvIhO%2F-MdVPTgt_CZYNfSpEpS6%2Fimage.png?alt=media\&token=85411c9f-e333-44bb-b440-8dcb2ba9e57c)

## **Rarity Table**

Below table displays the # of copies, roll probabilities, ETH values, and Azuki required to unbind for each Rarity from common to Legendary. The base value (ETH) for each non-binding failed NFT increases over time, as players perform unbinds.

| Rarity         | Uniques | Copies/Unique | Total Copies | Base Value (ETH) | Roll % | Azuki |
| -------------- | ------- | ------------- | ------------ | ---------------- | ------ | ----- |
| Legendary      | 15      | 60            | 900          | 0.831366 +       | 0.72%  | 100   |
| Ethereal       | 15      | 250           | 3750         | 0.185276 +       | 2.99%  | 25    |
| Mythic         | 20      | 500           | 10000        | 0.053445 +       | 7.97%  | 6     |
| Rare           | 25      | 1200          | 18125        | 0.0221151 +      | 14.44% | 3     |
| Common         | 25      | 30000         | 1200         | 0.0089075 +      | 23.90% | 1     |
| Binding Failed | 14      | \*            | 62735        | 0                | 50%    | N/A   |
| Jackpot        | 3       | 3             | 9            | 2.96916 +        | 0.007% | 500   |

For up-to-date stats, check the Chainbinders [official collection page](https://chainbinders.degacha.com/my-collection), or [this tracker made](https://q1mrn.csb.app/) by community member Ran.

## Links

* [Chainbinders Website](https://chainbinders.degacha.com/)
* [Chainbinders Twitter](https://twitter.com/chainbinders)
* [Chainbinders playlist on Youtube](https://www.youtube.com/watch?v=OLEVaQv7_kM\&list=PLwWk83tr8mUGDn12_UevEZ756DyMIMjbY\&index=3)
* [Detailed blog post about Chainbinders](https://dokidoki-finance.medium.com/introducing-chainbinders-a-next-generation-nft-gacha-experience-en-4eaaa942f329)


# Unicly (uDOKI)

### **Introduction**

[UNICLY](https://unic.ly/) is a permissionless protocol that lets people convert a NFT collection into “u” tokens, which are tradable ERC20s. uDOKI is a “u” token which represents fractionalized ownership of the official Doki Doki NFT collection on Unicly.

### **How UNICLY and uDOKI works:**

UNICLY has a [Uniswap-like DEX](https://www.app.unic.ly/#/swap) where u-tokens, including uDOKI can be traded. On the [uDOKI collection page](https://www.app.unic.ly/#/utoken-contract/0x7e6c38d007740931e4b419bf15a68c79a0fb0c66), people can bid ETH on individual pieces and uDOKI token holders can vote as a collective in order to “unlock” the collection. 51% of all token holders need to vote for the collection to be unlocked. When a collection is unlocked, NFTs are sold to the highest bidders, and uDOKI holders receive ETH in proportion to the quantity of uDOKI tokens they own.

### **Yield-farming UNIC with uDOKI**

As uDOKI is a whitelisted collection, you can provide and stake liquidity for uDOKI-ETH and uDOKI-UNIC to farm UNIC, the native governance and revenue-sharing token of the UNICLY protocol. Check out this [step by step tutorial](https://docs.unic.ly/tutorials/how-to-liquidity-mine-and-farm-unic) to learn how to get started!

### **uDOKI collection appraisal**

In the beginning, third party appraisal service [Upshot ](https://upshot.io/)valued the uDOKI collection at 75 ETH

As of 7/25/2021, the appraisal has been updated to 90 ETH. **Source:** [**uTOKEN Appraisals Spreadsheet** ](https://docs.google.com/spreadsheets/d/1bGdcv6XJRmg1vaVLDDo8h6XeEqAHJko1s83HuS1hR9w/edit#gid=0)

Additional rare NFTs are added to the uDOKI collection on a regular basis, which aims to increase the value of the uDOKI collection overtime. The collection log below records all the NFTs that have been added since the beginning.

### **uDOKI Tokenomics**

* Max Supply: 1,000,000,000
* Initial Liquidity (50%): 500,000,000
* Farming Rewards (40%): 400,000,000
* Doki Team Treasury(10%): 100,000,000
* Vote required to unlock collection: 51%

### **uDOKI collection log**

| Date      | NFT                           | Notes               |
| --------- | ----------------------------- | ------------------- |
| 4-08-2021 | Pixelated Retro Japan X3      | Genesis Set         |
| 4-15-2021 | Twilight Tea 3/3 by Seerlight | Donated by Rei      |
| 4-15-2021 | Doki Lambo Girl               | Donated by Rei      |
| 4-15-2021 | Doki Doki Finance             | Donated by Lumen    |
| 5-16-2021 | Pranksy Airways Ultimate      | Donated by Dokilord |
| 5-16-2021 | Streets of Ikebukuro          | Donated by Lumen    |
| 5-30-2021 | Cometh Spaceship "Anpan"      | Donated by Lumen    |

To learn more about how Unicly works, read the [Official Unicly Documentation](https://docs.unic.ly/)


