The Portal Contract

Portal Deployment

Toshi Mart is based on Flap Protocol V4 with an optional farcaster extension. The entrypoint of the protocol is a smart contract named Portal, all transactions and interactions with the Toshi Mart are routed through this contract.

The Portal contract is deployed on both Base Mainnet and Base Sepolia Testnet. The contract address is as follows:

Network
Portal Contract Address

Base Mainnet

0x1ea172Fb88C24DFC21Ff6Fa38762511C123bA948

Base Sepolia Testnet

0x561e4410DD8d4428Fb43127F1B19488F33523810

The portal entrypoint is verified on basescan.The interface and abi of the Portal contract can be found on basescan.

1. How To Launch A Token on Toshi Mart

1.1 Prepare The Metadata For Your Token

Toshi Mart assumes your token's metadata is stored on IPFS. The following is an example of the metadata for a token:

{
  "buy": null,
  "creator": "0x315F3803d4b33F9316be52297522EA7208a52f80",
  "description": "#JACKWILLTAKEUS ",
  "image": "bafkreic7gc6krk32vmtvd55f3n2wavevz6hcxsyeagxppgn3p2gwphafsi",
  "sell": null,
  "telegram": null,
  "twitter": null,
  "website": null
}

Note the image field is another IPFS hash of the image you want to use as the token's image.

Although you can use any IPFS gateway to upload and pin your metadata, we recomend using our upload endpoint for uploading the metadata json along with the image in a single request. Here is an typescript example of how to upload the metadata and image to Toshi Mart's upload endpoint:

/// Upload the meta of the token
async function uploadTokenMeta(cfg: {
    buy: string | null;
    creator: string;
    description: string;
    sell: string | null;
    telegram: string | null;
    twitter: string | null;
    website: string | null;
    image_path: string;
}) {

    const form = new FormData();

    const MUTATION_CREATE = `
    mutation Create($file: Upload!, $meta: MetadataInput!) {
      create(file: $file, meta: $meta)
    }
    `;

    form.append(
        "operations",
        JSON.stringify({
            query: MUTATION_CREATE,
            variables: {
                file: null, meta: {
                    website: cfg.website,
                    twitter: cfg.twitter,
                    telegram: cfg.telegram,
                    description: cfg.description,
                    creator: "0x0000000000000000000000000000000000000000",
                }
            },
        })
    );

    form.append(
        "map",
        JSON.stringify({
            "0": ["variables.file"],
        })
    );

    // read local file and convert to File object, with type "image/png"
    const file = new File([fs.readFileSync(cfg.image_path)], "image.png", {
        type: "image/png",
    });
    form.append("0", file);

    const res = await axios.postForm("https://beta-api.rcto.fun/", form, {
        headers: {
            "Content-Type": "multipart/form-data",
        },
    });

    if (res.status !== 200) {
        throw new Error(`failed to upload the token meta: ${res.statusText}`);
    }

    console.log("token meta uploading response: ", res.data);

    // the cid is in the response data 
    const cid = res.data.data.create;

    return cid;

}

1.2 Find The Vanity Salt

Every token launched on Toshi Mart now has a vanity ending: every token's CA ends with 8453. The salt for getting the vanity ending for your token contract should be provided on calling the newTokenV3 method of the Portal contract.

The resulting address of the token depends on the token_impl address. Every token contract launched on Toshi Mart is a clone of the token_impl contract, the token_impl contract is deployed on Base Mainnet and Base Sepolia Testnet. The contract address is as follows:

Network
Token Impl Contract Address

Base Mainnet

0xF3c514E04f83166E80718f29f0d34F206be40A0A

Base Sepolia Testnet

0x3B77b7AE7d925E59a346b0b552Bb6e4BEf417863

Here is a typescript example of how to find the vanity salt for your token to be launched on Toshi Mart:

import { Account, Address, createWalletClient, encodeAbiParameters, encodeDeployData, encodeFunctionData, formatEther, getContract, getContractAddress, Hex, hexToBigInt, http, keccak256, parseEther, parseGwei, parseTransaction, PublicClient, serializeTransaction, toBytes, toHex } from 'viem';


// mainnet info
const portal = "0x1ea172Fb88C24DFC21Ff6Fa38762511C123bA948" as Address; 
const token_impl = "0xF3c514E04f83166E80718f29f0d34F206be40A0A" as Address;

/// get vanity token address and salt 
async function findVanityTokenSalt(suffix: string) {
    if (suffix.length !== 4) {
        throw new Error("Suffix must be exactly 4 characters");
    }

    // predict the vanity token address based on the salt
    const predictVanityTokenAddress = (salt: Hex): Address => {
        const bytecode = '0x3d602d80600a3d3981f3363d3d373d3d3d363d73'
            + token_impl.slice(2).toLowerCase() // remove 0x prefix
            + '5af43d82803e903d91602b57fd5bf3' as Hex;

        return getContractAddress({
            from: portal,
            salt: toBytes(salt),
            bytecode,
            opcode: "CREATE2",
        });
    }

    // Note: you don't have to use a private key as the starting seed, you can use 
    // any pseudo-random string as the starting seed. 
    // 
    // Here, we use a random private key as the starting seed
    // Then repeatedly hash the seed until we get the vanity address
    const seed = generatePrivateKey();
    let salt = keccak256(toHex(seed));
    let iterations = 0; // Track the number of iterations 

    while (!predictVanityTokenAddress(salt).endsWith(suffix)) {
        salt = keccak256(salt);
        iterations++; // Increment the iteration count
    }

    console.log(`Iterations: ${iterations}`); // Print the number of iterations

    return {
        salt,
        address: predictVanityTokenAddress(salt),
    }
}

1.3 Send The Transaction To Launch The Token

To launch a token on Toshi Mart, you need to call the newTokenV3 method of the Portal contract in a transaction. The newTokenV3 method takes a NewTokenV3Params struct as the parameter, which contains the following fields:

  • name : The name of the token, e.g. The Test Token

  • symbol : The symbol of the token, e.g. TEST

  • meta : this is the ipfs cid you get from last section , eg, bafkreic7gc6krk32vmtvd55f3n2wavevz6hcxsyeagxppgn3p2gwphafsi

  • dexThresh : This determines the threshold to migrate token to DEX. Our UI uses FOUR_FIFTHS by default, which means when 80% or more of the total supply has been sold from the bonding curve , the token will be migrated to DEX.

  • taxRate : for Toshi Mart, this must always be 0.

  • migratorType : The migrator for the token. Our UI uses V3_MIGRATOR by default. With a V3_MIGRATOR we will optimize your liquidity and enable the revenue share feature for your token.

  • quoteToken : The quote token you want to use. When your quote token is the native gas token (i.e ETH), leave quoteToken as the zero address. Note: only enabled quoteToken can be used.

  • quoteAmt: The amount of quoteToken you want to spend to buy token on creation.

  • beneficiary : If the token uses a V3_MIGRATOR , the LP fee can later be claimed with this address after the token is migrated to DEX.

  • permitData : If your quoteToken is not zero address (i.e, not native gas token ETH , but some ERC20 token). You can construct the permitData to avoid another approve TX. Unluckily, TOSHI token does not support permit yet, so you need to approve TOSHI token before calling the newTokenV3 method. However, we may add other tokens as quote tokens in the future, so you can use the permitData to avoid another approve TX.

  • salt : The salt is mandate. All tokens created on Toshi Mart will have a vanity ending 8453, so you need to provide the salt that will generate the vanity ending. You can use the findVanityTokenSalt function in the previous section to get the salt.

  • extensionID : check the 1.4 A Note On The Extension section for more details on how to use the extension. If your payment token is TOSHI, this should never be empty.

  • extensionData : check the 1.4 A Note On The Extension section for more details on how to use the extension. If you are not using any extension, this can be empty.


/// @notice Create a new token (V3) with extension support
/// @param params The parameters for the new token including extension configuration
/// @return token The address of the created token
/// @dev Similar to newTokenV2 but with extension support. Extension hooks will be called if extensionID is non-zero
function newTokenV3(NewTokenV3Params calldata params) external payable returns (address token);



/// @notice Parameters for creating a new token (V3) with extension support
struct NewTokenV3Params {
    /// The name of the token
    string name;
    /// The symbol of the token
    string symbol;
    /// The metadata URI of the token
    string meta;
    /// The DEX supply threshold type
    DexThreshType dexThresh;
    /// The salt for deterministic deployment
    bytes32 salt;
    /// The tax rate in basis points (if non-zero, this is a tax token)
    uint16 taxRate;
    /// The migrator type (see MigratorType enum)
    MigratorType migratorType;
    /// The quote token address (native gas token if zero address)
    address quoteToken;
    /// The initial quote token amount to spend for buying
    uint256 quoteAmt;
    /// The beneficiary address for the token
    /// For rev share tokens, this is the address that can claim the LP fees
    /// For tax tokens, this is the address that receives the tax fees
    address beneficiary;
    /// The optional permit data for the quote token
    bytes permitData;
    /// @notice The ID of the extension to be used for the new token if not zero
    bytes32 extensionID;
    /// @notice Additional extension specific data to be passed to the extension's `onTokenCreation` method, check the extension's documentation for details on the expected format and content.
    bytes extensionData;
}


/// @notice the migrator type
/// @dev the migrator type determines how the liquidity is added to the DEX.
/// Note: To mitigate the risk of DOS, if a V3 migrator is used but the liquidity cannot
/// be added to v3 pools, the migrator will fallback to a V2 migrator.
/// A TAX token must use a V2 migrator.
enum MigratorType {
    V3_MIGRATOR, // Migrate the liquidity to a Uniswap V3 like pool
    V2_MIGRATOR // Migrate the liquidity to a Uniswap V2 like poo
}

/// @dev dex threshold types
enum DexThreshType {
    TWO_THIRDS, //  66.67% supply
    FOUR_FIFTHS, // 80% supply
    HALF, // 50% supply
    _95_PERCENT, // 95% supply
    _81_PERCENT, // 81% supply
    _1_PERCENT // 1% supply => mainly for testing

}

1.4 A Note On The Extension

The only supported extension for Toshi Mart is the Farcaster extension. The Farcaster extension allows you to attribute your actions (either launching a token or trading a token) to your Farcaster account (or the coming BASE app profile).

  • Currently, the extension does not support ETH pair. When your quote token is ETH (i.e zero address), both extensionID and extensionData should be empty.

  • If your quote token is TOSHI, the extensionID should be set to 0xde2fc1e2b9af1e3b4c78aa33ffb29301a87cd6208fff491445490b8df8a4effa and the extensionData is optional. If extensionData is not empty, you can provide the proof and attribute it to your Farcaster account.

Check the Farcaster Extension section for more details on how to use the Farcaster extension.

2. How To Trade A Token

2.1 Bonding Curve Fee

Currently, Toshi Mart charges a 1% fee on every trade on the bonding curve. The fee is taken from the quote token (i.e ETH or TOSHI).

That means if you buy 10000 TOSHI value of the token, only 9900 TOSHI will be added to the token's reserve, and 100 TOSHI will be taken as the fee. And if you sell 10000 TOSHI value of the token, only 9900 TOSHI will be sent to you, and 100 TOSHI will be taken as the fee.

2.2 Trade Migrated Tokens

Since v4.5.0, the portal contract has supported trades for migrated tokens. Fetching a quote and executing a swap are the same for both tokens that are still on the bonding curve and tokens that are already listed on Uniswap:

  • You can get a quote with quoteExactInput

  • You execute the trade by calling the swapExactInput method

And the sugar swap is also available:

  • If a token's quote token is TOSHI, you can still swap ETH for that token or the other way around.

2.3 Get A Quote

To get a quote , we call the quoteExactInput function of the Portal contract.

/// @notice Parameters for quoting the output amount for a given input
struct QuoteExactInputParams {
    /// @notice The address of the input token (use address(0) for native asset)
    address inputToken;
    /// @notice The address of the output token (use address(0) for native asset)
    address outputToken;
    /// @notice The amount of input token to swap (in input token decimals)
    uint256 inputAmount;
}

/// @notice Quote the output amount for a given input
/// @param params The quote parameters
/// @return outputAmount The quoted output amount
/// @dev refer to the swapExactInput method for the scenarios
function quoteExactInput(QuoteExactInputParams calldata params) external returns (uint256 outputAmount);

Note: the quoteExactInput method is not a view function, but we don’t need to send a transaction to get the quote (an eth_call or the simulation in viem will do the work) .

If the quote token is ETH, we construct the QuoteExactInputParams as follows:

If we are swapping ETH to the token, we set the inputToken to address(0) and the outputToken to the token's address. And the inputAmount is the amount of ETH we want to spend.

If we are swapping the token to ETH, we set the inputToken to the token's address and the outputToken to address(0). And the inputAmount is the amount of token we want to sell.

If the quote token is TOSHI, we construct the QuoteExactInputParams as follows:

If we are swapping TOSHI for the token, we set the inputToken to the TOSHI token address and the outputToken to the token's address. And the inputAmount is the amount of TOSHI we want to spend.

If we are swapping the token to TOSHI, we set the inputToken to the token's address and the outputToken to the TOSHI token address. And the inputAmount is the amount of token we want to sell.

2.4 Execute The Swap

After getting the quote, we can execute the swap by calling the swapExactInputV3 method of the Portal contract. The swapExactInputV3 method takes an ExactInputV3Params struct as the parameter, which contains the following fields:

  • inputToken : This can either be any of the quote token (i.e zero address for ETH or TOSHI) or the token you want to trade. If you want to sell the token, set this to the token's address. If you want to buy the token, set this to the quote token's address (i.e zero address for ETH or TOSHI).

  • outputToken : This can either be any of the quote token (i.e zero address for ETH or TOSHI) or the token you want to trade. If you want to sell the token, set this to the quote token's address (i.e zero address for ETH or TOSHI). If you want to buy the token, set this to the token's address.

  • inputAmount : The amount of input token to swap. If you want to buy the token, this is the amount of quote token you want to spend. If you want to sell the token, this is the amount of token you want to sell.

  • minOutputAmount : The minimum amount of output token you want to receive. This is to protect you from slippage. If the actual output amount is less than this, the transaction will revert.

  • permitData : As TOSHI does not support permit, this can only be used when selling the token. If you are selling the token, you can use the permitData to avoid another approve TX. If you are buying the token, this is not needed.

  • extensionData : Check the Farcaster Extension section for more details on how to use the Farcaster extension. (If a token is already listed on Uniswap, the extensionData will be ignored)


/// @notice Parameters for swapping exact input amount for output token (V3) with extension support
struct ExactInputV3Params {
    /// @notice The address of the input token (use address(0) for native asset)
    address inputToken;
    /// @notice The address of the output token (use address(0) for native asset)
    address outputToken;
    /// @notice The amount of input token to swap (in input token decimals)
    uint256 inputAmount;
    /// @notice The minimum amount of output token to receive
    uint256 minOutputAmount;
    /// @notice Optional permit data for the input token (can be empty)
    bytes permitData;
    /// @notice Additional extension specific data to be passed to the extension's `onTrade` method, check the extension's documentation for details on the expected format and content
    bytes extensionData;
}

/// @notice Parameters for quoting the output amount for a given input
struct QuoteExactInputParams {
    /// @notice The address of the input token (use address(0) for native asset)
    address inputToken;
    /// @notice The address of the output token (use address(0) for native asset)
    address outputToken;
    /// @notice The amount of input token to swap (in input token decimals)
    uint256 inputAmount;
}    

/// @notice Swap exact input amount for output token (V3) with extension support
/// @param params The swap parameters including extension data
/// @return outputAmount The amount of output token received
/// @dev Similar to swapExactInput but with extension support. Extension hooks will be called if the token uses an extension
function swapExactInputV3(ExactInputV3Params calldata params) external payable returns (uint256 outputAmount);

/// @notice Quote the output amount for a given input
/// @param params The quote parameters
/// @return outputAmount The quoted output amount
/// @dev refer to the swapExactInput method for the scenarios
function quoteExactInput(QuoteExactInputParams calldata params) external returns (uint256 outputAmount);

You can always first approve your token to be spent by the Portal contract before calling the swapExactInputV3 method. However, if you want to avoid another approve TX, you can use the permitData to approve the Portal contract to spend your token. And here is an example on how to generate the permitData for the TOSHI token:


async function generatePermitData(
        tokenAddress: Address,
        amount: bigint
    ): Promise<Hex>{
        if (!user || !publicClient) {
            throw new Error("User or public client not available");
        }

        const tokenContract = getContract({
            abi: [
                {
                    type: "function",
                    name: "name",
                    inputs: [],
                    outputs: [{ name: "", type: "string" }],
                    stateMutability: "view"
                },
                {
                    type: "function",
                    name: "nonces",
                    inputs: [{ name: "", type: "address" }],
                    outputs: [{ name: "", type: "uint256" }],
                    stateMutability: "view"
                }
            ],
            address: tokenAddress,
            client: publicClient,
        });

        const nonce = await tokenContract.read.nonces([user]) as bigint;
        const name = await tokenContract.read.name() as string;
        const deadline = BigInt(Date.now() + 10 * 60 * 1000); 

        const sig = await signTypedDataAsync({
            domain: {
                name,
                version: "1",
                chainId,
                verifyingContract: tokenAddress
            },
            types: {
                Permit: [
                    { name: "owner", type: "address" },
                    { name: "spender", type: "address" },
                    { name: "value", type: "uint256" },
                    { name: "nonce", type: "uint256" },
                    { name: "deadline", type: "uint256" }
                ]
            },
            primaryType: "Permit",
            message: {
                owner: user,
                spender: PORTAL,
                value: amount,
                nonce,
                deadline,
            }
        });

        // Parse signature into r, s, v
        const { r, s, v } = parseSignature(sig) as {
            r: `0x${string}`;
            s: `0x${string}`;
            v: bigint;
            yParity: number;
        };

        // Encode permit data
        const permitData = encodeAbiParameters([
            { name: "owner", type: "address" },
            { name: "spender", type: "address" },
            { name: "value", type: "uint256" },
            { name: "deadline", type: "uint256" },
            { name: "v", type: "uint8" },
            { name: "r", type: "bytes32" },
            { name: "s", type: "bytes32" }
        ], [user, PORTAL, amount, deadline, Number(v), r, s]);

        return permitData;
    }

3. The Farcaster Extension

The farcaster extension allows you to optionally attribute your actions (either launching a token or trading a token) to your Farcaster account (or the coming BASE app profile). If you are trading or launching the token in our mini app, the Farcaster extension will be automatically enabled for you.

Besides trading and launching tokens in mini app, you can also use our verify tool to bind any of your EVM address to your farcaster account. Toshi Mart will prefer showing your Farcaster account if you have bound your EVM address to it.

3.1 Bind Your EVM Address To Your Farcaster Account

When you connect your EVM address (including Farcaster Wallet) to Toshi Mart Mini App and trade or launch a token once, your EVM address will be automatically bound to your Farcaster account.

And we also allows you to programmatically bind your EVM address to your Farcaster account. To do this, you need to get a farcaster proof first.

Here is how you can do it:

  1. Open the Flap Farcaster Verify Mini App: https://farcaster.xyz/miniapps/i_eTs1A8VUeO/verify

  2. Then you can either switch the wallet that you want to use in your program or directly input an EVM address you want to use in your program. And do not forget to check the mainnet tab or you will get a testnet proof. Then click the “Get Flap Farcaster Verification Proof” button

  1. Optionally, you may see the following image. In such case, you need to open your farcaster mobile app to approve the authentification.

  1. Then you will see the proof returned. Then you can use the proof along with the specified address. When you interact with our smart contract with that address, our indexer will attribute those actions to your farcaster account.

Here is an example of launching token with Farcaster extension, the extension data is the abi encoding of the OnTokenCreationData struct, which contains the fid of your Farcaster account, a farcasterOnly flag (which is 1 for farcaster only mode), and the proof you obtained earlier.

    
    // keccak256("flap.extension.farcaster.v1") 
    const farcaster_ext_id = "0xde2fc1e2b9af1e3b4c78aa33ffb29301a87cd6208fff491445490b8df8a4effa" as Hex;
    const params = {
        name: "Farcaster Only Demo Token",
        symbol: "FCDEMO",
        meta: metaCid,
        dexThresh: DexThreshType.FOUR_FIFTHS,
        salt: salt,
        taxRate: 0, // can only be zero for now 
        migratorType: MigratorType.V3_MIGRATOR,
        quoteToken: toshiToken,
        quoteAmt: parseEther("0"),
        beneficiary: account.address,
        permitData: "0x",
        extensionID: farcaster_ext_id,
        extensionData: encodeAbiParameters(
            [
                {
                    name: "OnTokenCreationData",
                    type: "tuple",
                    components: [
                        { name: "fid", type: "uint32" },
                        { name: "farcasterOnly", type: "uint8" },
                        { name: "proof", type: "bytes" }
                    ]
                }
            ],
            [
                {
                    fid: proof.fid,
                    farcasterOnly: 0, // can only be zero since restriction is removed in production release 
                    proof: proof.proof as Hex,
                }
            ]
        )
    };

And here is an example of trading a token with Farcaster extension, the extension data is the abi encoding of the OnTradeData struct, which contains the fid of your Farcaster account and the proof you obtained earlier.


  const swapParams = {
      inputToken: FlapBaseSepoliaConfig.toshi,
      outputToken: TEST_TOKEN_ADDRESS,
      inputAmount: TRADE_AMOUNT,
      minOutputAmount: minOutputAmount,
      permitData: "0x" as Hex, // toshi does not support permit
      // You can use empty "0x" hex if the token is not farcaster only 
      extensionData: encodeAbiParameters(
          [
              {
                  name: "OnTradeData",
                  type: "tuple",
                  components: [
                      { name: "fid", type: "uint32" },
                      { name: "proof", type: "bytes" }
                  ]
              }
          ],
          [
              {
                  fid: proof.fid,
                  proof: proof.proof as Hex,
              }
          ]
      )
  };

3.2 Profile Merging

If you have bound multiple EVM addresses to the same Farcaster account, they will be merged into a single profile in Toshi Mart Mini App. This means that all your actions (trading, launching tokens, etc.) will be attributed to the same Farcaster account, regardless of which EVM address you used to perform the action.

4. How To Build An Indexer For Toshi Mart Tokens

4.1 Index Token Creation Events

For backward compatibility, each newly launched token will emit one or more events rather than a single event, these events are emitted from the Portal Contract:

  • TokenCreated : Every token launch will emit a TokenCreated event.

  • TokenCurveSet : (optional), if a token launch does not emit this event, its curve is the first one in the CurveType enum, (i.e, the one with a value = 0)

  • TokenDexSupplyThreshSet : (optional), if a token does not emit this event, its dex threshold is by default the first one in the DexThreshType enum, which is 6.67e8 ether.

  • TokenQuoteSet : (optional), if a token does not emit this event, its quote token is the native gas token (i.e, zero address)

  • TokenMigratorSet : (optional), if a token does not emit this event, its Migrator type is by default V3_MIGRATOR (i.e, the first one in the MigratorType enum).

  • FlapTokenTaxSet : (optional), if a token does not emit this event, the tax is 0 or it is not a tax token.

  • TokenExtensionEnabled : emitted if only the token is created with an extension (currently, we only support farcaster extension)


/// @notice emitted when a new token is created
///
/// @param ts The timestamp of the event
/// @param creator The address of the creator
/// @param nonce The nonce of the token
/// @param token  The address of the token
/// @param name  The name of the token
/// @param symbol  The symbol of the token
/// @param meta The meta URI of the token
event TokenCreated(
  uint256 ts, address creator, uint256 nonce, address token, string name, string symbol, string meta
);

/// emitted when a token's curve is set
/// @param token The address of the token
/// @param curve The address of the curve
/// @param curveParameter The parameter of the curve
event TokenCurveSet(address token, address curve, uint256 curveParameter);

/// emitted when a token's dexSupplyThresh is set
/// @param token The address of the token
/// @param dexSupplyThresh The new dexSupplyThresh of the token
event TokenDexSupplyThreshSet(address token, uint256 dexSupplyThresh);

/// emitted when a token's implementation is set
/// @param token The address of the token
/// @param version The version of the token
event TokenVersionSet(address token, TokenVersion version);

/// @notice emitted when a token's quote token is set
/// @param token The address of the token
/// @param quoteToken The address of the quote token
event TokenQuoteSet(address token, address quoteToken);

/// @notice emitted when a token's migrator is set
/// @param token The address of the token
/// @param migratorType The migrator type
event TokenMigratorSet(address token, MigratorType migratorType); 


/// @notice emitted when a new tax is set for a token
/// @param token The address of the token
/// @param tax The tax value set for the token
event FlapTokenTaxSet(address token, uint256 tax); 


/// @notice emitted when a token's extension is enabled
/// @param token The address of the token
/// @param extensionID The extension ID
/// @param extensionAddress The address of the extension contract
/// @param version The version of the extension
event TokenExtensionEnabled(address token, bytes32 extensionID, address extensionAddress, uint8 version);

4.2 How To Get Token Meta and Image

As we have mentioned in the 1.1 Prepare The Metadata For Your Token section, the metadata of a token is stored on IPFS. You can get the metadata by fetching the IPFS CID from the TokenCreated event.

Alternatively, you can also get the metadata through the token's contract by calling the metaURI method of the token contract. The meta method returns the IPFS CID of the token's metadata.

interface IFlapToken {
    function metaU RI() external view returns (string memory);
}

Let's take the JACK token as an example to illustrate how to get the image:

If you call the metaURI method of the 0x8d760f4fda919a8e9f38237ee003fe8ff0ca9ef7 token, you will get the following cid: bafkreihxg3ostaaavhg7bjxmju53hzbmqr3uczsytkctqj2aqnmocdq5iy.

You can get the meta json from any ipfs gateway. However, it would be fast to use our ipfs gateway. Our gateway is located at https://flap.mypinata.cloud/ . Such that, you can get the meta json here: https://flap.mypinata.cloud/ipfs/bafkreihxg3ostaaavhg7bjxmju53hzbmqr3uczsytkctqj2aqnmocdq5iy.

The meta json is as follows:


{
  "buy": null,
  "creator": "0x315F3803d4b33F9316be52297522EA7208a52f80",
  "description": "#JACKWILLTAKEUS ",
  "image": "bafkreic7gc6krk32vmtvd55f3n2wavevz6hcxsyeagxppgn3p2gwphafsi",
  "sell": null,
  "telegram": null,
  "twitter": null,
  "website": null
}

Note that the image field is also a cid, which is the cid of the image. Similarly, we can get the image here: https://flap.mypinata.cloud/ipfs/bafkreic7gc6krk32vmtvd55f3n2wavevz6hcxsyeagxppgn3p2gwphafsi.

4.2 Index Token Trade Events

When a token is traded on the bonding curve, the Portal contract will emit one of the following events:

  • TokenBought : emitted when a token is bought on the bonding curve.

  • TokenSold : emitted when a token is sold on the bonding curve.


/// @notice emitted when a token is bought
///
/// @param ts The timestamp of the event
/// @param token  The address of the token
/// @param buyer  The address of the buyer
/// @param amount  The amount of tokens bought
/// @param eth  The amount of quote token (ETH / TOSHI) spent
/// @param fee The amount of quote token (ETH / TOSHI) spent on fee
/// @param postPrice The price of the token after this trade
event TokenBought(
    uint256 ts, address token, address buyer, uint256 amount, uint256 eth, uint256 fee, uint256 postPrice
);

/// @notice emitted when a token is sold
///
/// @param ts The timestamp of the event
/// @param token  The address of the token
/// @param seller  The address of the seller
/// @param amount  The amount of tokens sold
/// @param eth  The amount of quote token (ETH / TOSHI) received
/// @param fee  The amount of quote token (ETH / TOSHI) deducted as a fee
/// @param postPrice The price of the token after this trade
event TokenSold(
    uint256 ts, address token, address seller, uint256 amount, uint256 eth, uint256 fee, uint256 postPrice
);

Besides, the following event will emmited:


/// @notice emitted when the circulating supply of a token changes
/// @param token The address of the token
/// @param newSupply The new circulating supply
event FlapTokenCirculatingSupplyChanged(address token, uint256 newSupply);

With the FlapTokenCirculatingSupplyChanged event, you can track the circulating supply of a token and make use of it to get a quote off-chain with the bonding curve equation.

4.3 Index Token Migration Events

When a token is migrated to DEX, the Portal contract will emit the following events:


/// @notice emitted when migrating to DEX 
/// @param token The address of the token
/// @param pool The address of the pool
/// @param amount The amount of token added to DEX 
/// @param eth The amount of quote token (ETH / TOSHI) added to DEX 
event LaunchedToDEX(address token, address pool, uint256 amount, uint256 eth);

Last updated