I have a Rust Anchor program for swapping SPL Tokens. It has three methods: Make, refund and take. Also I have a Next.js frontend app. I want to integrate this Rust Anchor program with Next.js app. For the first time, I want to integrate make method of the program.
Here is the make.rs file.
use anchor_lang::prelude::*;
use anchor_spl::{
associated_token::AssociatedToken,
token_interface::{transfer_checked, Mint, TokenAccount, TokenInterface, TransferChecked},
};
use crate::Escrow;
#[derive(Accounts)]
#[instruction(seed: u64)]
pub struct Make<'info> {
#[account(mut)]
pub maker: Signer<'info>,
#[account(
mint::token_program = token_program
)]
pub mint_a: InterfaceAccount<'info, Mint>,
#[account(
mint::token_program = token_program
)]
pub mint_b: InterfaceAccount<'info, Mint>,
#[account(
mut,
associated_token::mint = mint_a,
associated_token::authority = maker,
associated_token::token_program = token_program
)]
pub maker_ata_a: InterfaceAccount<'info, TokenAccount>,
#[account(
init,
payer = maker,
space = 8 + Escrow::INIT_SPACE,
seeds = [b"escrow", maker.key().as_ref(), seed.to_le_bytes().as_ref()],
bump
)]
pub escrow: Account<'info, Escrow>,
#[account(
init,
payer = maker,
associated_token::mint = mint_a,
associated_token::authority = escrow,
associated_token::token_program = token_program
)]
pub vault: InterfaceAccount<'info, TokenAccount>,
pub associated_token_program: Program<'info, AssociatedToken>,
pub token_program: Interface<'info, TokenInterface>,
pub system_program: Program<'info, System>,
}
impl<'info> Make<'info> {
pub fn save_escrow(&mut self, seed: u64, receive: u64, bumps: &MakeBumps) -> Result<()> {
self.escrow.set_inner(Escrow {
seed,
maker: self.maker.key(),
mint_a: self.mint_a.key(),
mint_b: self.mint_b.key(),
receive,
bump: bumps.escrow,
});
Ok(())
}
pub fn deposit(&mut self, deposit: u64) -> Result<()> {
let transfer_accounts = TransferChecked {
from: self.maker_ata_a.to_account_info(),
mint: self.mint_a.to_account_info(),
to: self.vault.to_account_info(),
authority: self.maker.to_account_info(),
};
let cpi_ctx = CpiContext::new(self.token_program.to_account_info(), transfer_accounts);
transfer_checked(cpi_ctx, deposit, self.mint_a.decimals)
}
}
In lib.rs file make()
method is implemented like that.
use anchor_lang::prelude::*;
pub mod contexts;
use contexts::*;
pub mod state;
pub use state::*;
declare_id!("avfosQBePj8eH7CSzbEenD5kCk4DEnNpg3if6dCdPrc");
#[program]
pub mod anchor_escrow {
use super::*;
pub fn make(ctx: Context<Make>, seed: u64, deposit: u64, receive: u64) -> Result<()> {
ctx.accounts.deposit(deposit)?;
ctx.accounts.save_escrow(seed, receive, &ctx.bumps)
}
...
In frontend part, I tried to implement integrating this method like that:
import Modal from '@/components/Common/Modal'
import { modalStore } from '@/hooks/useStore';
import getFormattedDateTime from '@/hooks/useCurrentDate'
import React, { useState, useEffect } from 'react'
import redstone from "redstone-api";
//import { createListing } from '@/services/ApiService';
import { AnchorWallet, useWallet } from '@solana/wallet-adapter-react';
import idl from "@/target/idl/anchor_escrow.json";
import { AnchorEscrow } from '@/target/types/anchor_escrow';
import * as anchor from "@coral-xyz/anchor";
import { clusterApiUrl, Connection, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js';
import { randomBytes } from 'crypto';
import { getAssociatedTokenAddress, getAssociatedTokenAddressSync, TOKEN_2022_PROGRAM_ID } from "@solana/spl-token";
const CreateListing = () => {
const modalKey = 'createListing';
const { toggleModal } = modalStore();
const [solToUSD, setSolToUSD] = useState(0.0);
const [solToETH, setSolToETH] = useState(0.0);
const wallet = useWallet();
const [amount, setAmount] = useState(0);
const [mintAddress, setMintAddress] = useState("");
const fetchSolToUSD = async () => {
const price = await redstone.getPrice("SOL");
setSolToUSD(price.value);
}
const fetchSolToETH = async () => {
const solPrice = await redstone.getPrice("SOL");
const ethPrice = await redstone.getPrice("ETH");
const solToEth = solPrice.value / ethPrice.value;
setSolToETH(solToEth);
}
const handleClose = () => {
toggleModal(modalKey);
}
const handleMake = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const idlJson = idl as anchor.Idl;
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
const provider = new anchor.AnchorProvider(connection, wallet as AnchorWallet);
const program = new anchor.Program<AnchorEscrow>(JSON.parse(JSON.stringify(idlJson)), provider);
const tokenProgram = TOKEN_2022_PROGRAM_ID;
if (!wallet?.publicKey) {
throw new Error("Wallet public key is null");
}
const seed = new anchor.BN(randomBytes(8));
const maker = wallet.publicKey;
const mintA = new PublicKey(mintAddress);
const mintB = new PublicKey(mintAddress);
const escrow = PublicKey.findProgramAddressSync(
[Buffer.from("escrow"), maker.toBuffer(), seed],
program.programId
)[0];
const makerAtaA = await getAssociatedTokenAddress(
mintA,
maker
);
const vault = getAssociatedTokenAddressSync(mintA, escrow, true);
try {
const tx = await program.methods
.make(seed, new anchor.BN(amount * LAMPORTS_PER_SOL), new anchor.BN(amount * LAMPORTS_PER_SOL))
.accounts(
{
maker,
mintA,
mintB,
makerAtaA: makerAtaA,
vault,
tokenProgram: tokenProgram
}
)
.rpc();
console.log(tx);
}
catch (err) {
console.error(err);
}
}
useEffect(() => {
fetchSolToUSD();
fetchSolToETH();
}, [])
return (
<Modal>
<div className="w-[577px] flex-col rounded-3xl bg-zinc-100 p-10 text-lg text-black">
<div className="rounded-2xl bg-neutral-800 px-5 py-2 text-white">
<div className="flex flex-row justify-between">
<div className="">{solToUSD !== 0.0 ? `1 SOL = ${solToUSD.toFixed(3)} USD` : 'Loading...'}</div>
<div>{getFormattedDateTime()}</div>
</div>
<div className="flex flex-row justify-between">
<div>{solToETH !== 0.0 ? `1 SOL = ${solToETH.toFixed(3)} ETH` : 'Loading...'}</div>
<div>{getFormattedDateTime()}</div>
</div>
</div>
<div className="mt-8 grid grid-cols-2">
<div className="rounded-bl-3xl rounded-tl-3xl border border-blue-600 bg-blue-600 py-1 text-center text-white">
Buy SOL
</div>
<div className="rounded-br-3xl rounded-tr-3xl border-2 border-blue-600 bg-white py-1 text-center text-blue-600">
Sell SOL
</div>
</div>
<form onSubmit={handleMake} className="flex flex-col">
<select
className="mt-8 rounded-3xl border border-zinc-300 bg-white text-neutral-500"
name="paymentMethod"
defaultValue={'DEFAULT'}
>
<option hidden disabled value="DEFAULT">
Payment Method
</option>
<option value="creditCard">Credit Card</option>
<option value="bankCard">Bank Card</option>
<option value="paypal">Paypal</option>
<option value="applePay">Apple Pay</option>
</select>
<select
className="mt-8 rounded-3xl border border-zinc-300 bg-white text-neutral-500"
name="country"
defaultValue={'DEFAULT'}
>
<option hidden disabled value="DEFAULT">
Country
</option>
<option value="tr">Turkey</option>
<option value="us">United States</option>
<option value="uk">United Kindom</option>
<option value="de">Germany</option>
</select>
<select
className="mt-8 rounded-3xl border border-zinc-300 bg-white text-neutral-500"
name="country"
defaultValue={'DEFAULT'}
onChange={(e) => setMintAddress(e.target.value)}
>
<option hidden disabled value="DEFAULT">
Token
</option>
<option id='USDC' value="Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr">USDC</option>
<option id='TET' value="HY6uvCfBQhKANRxBcYLBK7aUva8mT7mLP2SjrLLmipza">Tether</option>
</select>
<input
className="mt-8 rounded-3xl border border-zinc-300 bg-white text-neutral-500"
type="text"
placeholder="Wallet Address"
value={String(wallet.publicKey)}
readOnly
/>
<div className="relative mt-8">
<input
className="w-full rounded-full border-zinc-300 bg-white text-neutral-500 px-5 py-3 backdrop-blur-sm"
placeholder="Amount"
type="number"
onChange={(e) => setAmount(Number(e.target.value))}
/>
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
<span className="rounded-full bg-green-400 px-4 py-1 font-bold text-black">
SOL
</span>
</div>
</div>
<label className="text-sm opacity-30 ml-8 mt-2">Minimum: 10 SOL</label>
<div className="relative mt-5">
<input
className="w-full rounded-full border-zinc-300 bg-white text-neutral-500 px-5 py-3 backdrop-blur-sm"
placeholder="Price"
/>
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
<span className="rounded-full bg-green-400 px-4 py-1 font-bold text-black">
USD
</span>
</div>
</div>
<label className="text-sm opacity-30 ml-8 mt-2">Minimum: 10 USD</label>
{/*<div className="mt-7 ml-4 flex flex-row gap-5">
<div className="flexCenter group rounded-[10px]">
<input
type="checkbox"
className="h-6 w-6 cursor-pointer appearance-none rounded-[10px] bg-teal-400 text-teal-400 focus:ring-teal-100"
/>
</div>
<span className="text-lg font-medium text-neutral-500">
Will the offer be accepted automatically?
</span>
</div>*/}
<div className="flex justify-center text-white gap-4 mt-7">
<button type="reset" onClick={handleClose} className="p-2 w-56 rounded-full bg-gray-600">Cancel</button>
<button type="submit" className="p-2 w-56 rounded-full bg-blue-600">Save Listing</button>
</div>
</form>
</div>
</Modal>
)
}
export default CreateListing
However, when I submit the form, I got the error from console:
transactionLogs
:
Array(11)
0
:
"Program ComputeBudget111111111111111111111111111111 invoke [1]"
1
:
"Program ComputeBudget111111111111111111111111111111 success"
2
:
"Program ComputeBudget111111111111111111111111111111 invoke [1]"
3
:
"Program ComputeBudget111111111111111111111111111111 success"
4
:
"Program avfosQBePj8eH7CSzbEenD5kCk4DEnNpg3if6dCdPrc invoke [1]"
5
:
"Program log: Instruction: Make"
6
:
"Program 11111111111111111111111111111111 invoke [2]"
7
:
"Program 11111111111111111111111111111111 success"
8
:
"Instruction references an unknown account FeBFCyJ26WxmN61bnXB2CyxFaUV2PzGwvqerCJcT5sC4"
9
:
"Program avfosQBePj8eH7CSzbEenD5kCk4DEnNpg3if6dCdPrc consumed 20882 of 199700 compute units"
10
:
"Program avfosQBePj8eH7CSzbEenD5kCk4DEnNpg3if6dCdPrc failed: An account required by the instruction is missing"
Could you please help me to solve this problem?