Skip to content

Commit

Permalink
Merge pull request #289 from Build-Squad/ruben/update-solana-client-m…
Browse files Browse the repository at this point in the history
…ock-ui-v1

Update Solana Xfluencer Mock UI to interact with Escrows Using SPL
  • Loading branch information
rcolomina authored Apr 2, 2024
2 parents ac5f0d8 + 7e2a9db commit 6fda19e
Show file tree
Hide file tree
Showing 5 changed files with 323 additions and 17 deletions.
148 changes: 148 additions & 0 deletions solana-client/components/CancelEscrowSpl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import {
Connection,
PublicKey,
Transaction,
clusterApiUrl,
SYSVAR_RENT_PUBKEY
} from "@solana/web3.js"

import {
getAssociatedTokenAddress,
TOKEN_PROGRAM_ID,
} from '@solana/spl-token';

import {
AnchorProvider,
setProvider,
BN
} from "@project-serum/anchor"

import styles from '../styles/PingButton.module.css'
import idl from "../xfluencer.json"

import { useAnchorWallet, useWallet } from '@solana/wallet-adapter-react';
import * as anchor from "@coral-xyz/anchor";

import { FC } from "react";
import { utf8 } from "@coral-xyz/anchor/dist/cjs/utils/bytes";

import * as utils from "./utils";
import { log } from "console";

const programId = new PublicKey(idl.metadata.address);

export const findATA = (
walletKey: PublicKey,
mintKey: PublicKey,
): Promise<PublicKey> => {
return getAssociatedTokenAddress(
mintKey,
walletKey,
true, // allowOwnerOffCurve aka PDA
);
};

interface CancelEscrowSplProps {
business: string,
validatorAuthority: string,
mintTokenAccount: string,
orderCode: number
}

export const CancelEscrowSpl: FC<CancelEscrowSplProps> = (
{business,
validatorAuthority,
mintTokenAccount,
orderCode}) => {

const wallet = useAnchorWallet()
const connection = new Connection(clusterApiUrl('devnet'),
{
commitment: "confirmed",
confirmTransactionInitialTimeout: 30000
});

const program = utils.getAnchorProgram(connection);
const provider = new AnchorProvider(connection, wallet, {})
setProvider(provider)
const { publicKey, signTransaction, sendTransaction } = useWallet()

if (!connection || !publicKey) {
console.warn("Wallet was not connected")
return (
<div className={styles.buttonContainer}>
<button className={styles.button} disabled>CREATE (wallet not connected)</button>
</div> )
}

const onClick = async () => {

// transform strings to publick keys
const business_pk = new PublicKey(business)
const validatorAuthorityPk = new PublicKey(validatorAuthority) ;
const mintPublicKey = new PublicKey(mintTokenAccount);

console.log(business_pk.toString())

// discover ATA for Business
const associatedTokenAccForBusiness
= await findATA(business_pk, mintPublicKey);

// discover PDA for vault token account
const [vaultAccountPda, vault_account_bump]
= await PublicKey.findProgramAddress(
[Buffer.from(anchor.utils.bytes.utf8.encode("token-seed"+orderCode.toString()))],
program.programId
);

// discover PDA for escrow (data) account
const [escrowAccountPda, ]
= await PublicKey.findProgramAddress(
[Buffer.from(anchor.utils.bytes.utf8.encode("escrow-data"+orderCode.toString()))],
program.programId
);

// prepare instruction for cancel escrow using SPL
const ix = await program.methods.cancelEscrowSpl(
new BN(orderCode)
)
.accounts({
business: business_pk,
businessDepositTokenAccount: associatedTokenAccForBusiness,
vaultAccount: vaultAccountPda,
vaultAuthority: validatorAuthorityPk,
escrowAccount: escrowAccountPda,
tokenProgram: TOKEN_PROGRAM_ID,
rent: SYSVAR_RENT_PUBKEY,
})
.instruction();

const tx = new Transaction().add(ix);

const options = {
skipPreflight: true
}

try {
const signature = await sendTransaction(tx, connection, options);
const txSign = await connection.confirmTransaction(signature, "processed");
console.debug("txSing", txSign);
console.debug("context", txSign.context);
console.debug("value", txSign.value);
if(txSign.value.err != null){
throw new Error(`Instruction error number found: ` + txSign.value.err['InstructionError'][0].toString());
}
}
catch(error)
{
console.error(error)
}
}

return (
<button className={styles.button} onClick={onClick}>
Cancel Business (SPL) {business}
</button>
)

}
24 changes: 12 additions & 12 deletions solana-client/components/CreateEscrowSpl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,21 +104,22 @@ export const CreateEscrowSpl: FC<CreateEscrowSplProps> = ({validator,
console.log("Program Id", programId.toString())

const [escrow_account_pda, _escrow_account_bump]
= await PublicKey.findProgramAddress([
utf8.encode('escrow'),
utf8.encode(orderCode.toString())
],
programId
);
= await PublicKey.findProgramAddress([
utf8.encode('escrow-data'),
utf8.encode(orderCode.toString())
],
programId
);

console.log("Program Id", programId)
console.info("Escrow Address found:",
escrow_account_pda.toString(), _escrow_account_bump)

const VAULT_SEED = Buffer.from("vault" + orderCode.toString(),"utf8");
escrow_account_pda.toString(),
_escrow_account_bump)

const [_vault_account_pda, _vault_account_bump]
= await PublicKey.findProgramAddress(
[VAULT_SEED],programId);
[Buffer.from("token-seed" + orderCode.toString(),"utf8")],
programId);

console.info("vault",_vault_account_pda.toString(),_vault_account_bump)

Expand Down Expand Up @@ -149,15 +150,14 @@ export const CreateEscrowSpl: FC<CreateEscrowSplProps> = ({validator,
new BN(orderCode))
.accounts(
{
initializer: businessPublicKey,
business: businessPublicKey,
influencer: influencerPublicKey,
vaultAccount: vault_account_pda,
validationAuthority: validatorPublicKey,
mint: mintPublicKey,
businessDepositTokenAccount: businessTokenAccount,
influencerReceiveTokenAccount: influencerTokenAccount,
escrowAccount: escrow_account_pda,
vaultAccount: vault_account_pda,
systemProgram: SystemProgram.programId,
tokenProgram: TOKEN_PROGRAM_ID,
rent: SYSVAR_RENT_PUBKEY,
Expand Down
134 changes: 134 additions & 0 deletions solana-client/components/ValidateEscrowSpl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import {
Connection,
PublicKey,
Transaction,
clusterApiUrl,
} from "@solana/web3.js"

import {
AnchorProvider,
setProvider,
} from "@project-serum/anchor"

import styles from '../styles/PingButton.module.css'

import idl from "../xfluencer.json"
import {
useAnchorWallet,
useConnection,
useWallet
} from '@solana/wallet-adapter-react';
import * as anchor from "@coral-xyz/anchor";

import { FC } from "react";
import { utf8 } from "@coral-xyz/anchor/dist/cjs/utils/bytes";

import * as utils from "./utils";
import { TOKEN_PROGRAM_ID } from "@coral-xyz/anchor/dist/cjs/utils/token";

const programId = new PublicKey(idl.metadata.address);

interface ValidateEscrowSplProps {
validator: string,
business: string,
influencer: string,
percentageFee: number,
orderCode: number,
targetState: number,
textButton: string
}

export const ValidateEscrowSpl: FC<ValidateEscrowSplProps> = ({
validator,
business, influencer,
percentageFee, orderCode,
targetState, textButton }) => {

const wallet = useAnchorWallet()
const connection = new Connection(clusterApiUrl('devnet'),
{
commitment: "confirmed",
confirmTransactionInitialTimeout: 30000
});

const program = utils.getAnchorProgram(connection);
const provider = new AnchorProvider(connection, wallet, {})
setProvider(provider)
const { publicKey, signTransaction, sendTransaction } = useWallet()

if (!connection || !publicKey) {
const msg = "Wallet is not connected"
console.warn(msg)
return (
<div className={styles.buttonContainer}>
<button className={styles.button} disabled>{textButton} ({msg})</button>
</div>
)
} else {
console.log("wallet", wallet.publicKey)
}


const onClick = async () => {

if (!connection || !publicKey) {
console.warn("Wallet is not connected")
return
}

const validatorPublicKey = new PublicKey(validator);
const businessPublicKey = new PublicKey(business);
const influencerPublicKey = new PublicKey(influencer);

const [vaultAccountPda] = await PublicKey.findProgramAddress(
[Buffer.from("token-seed" + orderCode.toString(),"utf8")],
programId);

const [escrowPDA] = await PublicKey.findProgramAddress([
utf8.encode('escrow-data'),
utf8.encode(orderCode.toString())],programId);

const ix = await program.methods.validateEscrowSpl(
new anchor.BN(targetState),
new anchor.BN(percentageFee)
).accounts({
validationAuthority: validatorPublicKey,
vaultAccount: vaultAccountPda,
influencer: influencerPublicKey,
business: businessPublicKey,
escrowAccount: escrowPDA,
tokenProgram: TOKEN_PROGRAM_ID
}).instruction();

const tx = new Transaction().add(ix);

const options = {
skipPreflight: true
}

try {
const signature = await sendTransaction(tx, connection, options);
console.debug("signature", signature.valueOf());
const txSign = await connection.confirmTransaction(signature, "processed");
console.debug("txSing", txSign.value);
console.debug("context", txSign.context);
console.debug("value", txSign.value);
if (txSign.value.err != null) {
throw new Error(`Instruction error number found: ` + txSign.value.err['InstructionError'][0].toString());
}
}
catch (error) {
console.error(error)
alert("Error Found on Validation " + error);
}


}

return (
<button className={styles.button} onClick={onClick}>
{textButton}
</button>
)

}
28 changes: 24 additions & 4 deletions solana-client/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ import { AppBar } from '../components/AppBar'

import Head from 'next/head'
import {Input} from "@nextui-org/react";

// import components to support escrow using sol
import { CreateEscrowSolana } from '../components/CreateEscrowSolana'
import { ClaimEscrowSolana } from '../components/ClaimEscrowSolana'
import { CancelEscrowSolana } from '../components/CancelEscrowSolana'
import { CreateEscrowSpl } from '../components/CreateEscrowSpl'
import { Validate } from '../components/Validate'

// import components to support escrows using spl
import { CreateEscrowSpl } from '../components/CreateEscrowSpl'
import { CancelEscrowSpl } from '../components/CancelEscrowSpl'
import { ValidateEscrowSpl } from '../components/ValidateEscrowSpl'

import { LAMPORTS_PER_SOL } from '@solana/web3.js'
import { findATA } from "../components/utils";

Expand All @@ -33,7 +39,7 @@ const Home: NextPage = (props) => {
const NUM_SOLS : number = 0.1;
const LAMPORTS : number = NUM_SOLS * LAMPORTS_PER_SOL; // (10^9 lamports == 1 SOL)
const ORDER_CODE : number = 12347 // THIS MUST BE UNIQUE PER business-influencer (1 transaction at a time) OTHERWISE ERROR
const NUM_SPL_TOKENS : number = 1000000; // with 6 deciamls 10**6 is 1 token unit
const NUM_SPL_TOKENS : number = 1000000; // 6 decimals ==> 10 ** 6 == 1 Token Unit
const PERCENTAGE_FEE: number = 0;


Expand Down Expand Up @@ -103,8 +109,6 @@ const Home: NextPage = (props) => {
orderCode={ORDER_CODE} targetState={2} textButton={"Validate Escrow Delivery"}/>

<ClaimEscrowSolana business={BUSINESS} influencer={INFLUENCER} orderCode={ORDER_CODE} />
<CancelEscrowSolana business={BUSINESS} influencer={INFLUENCER} orderCode={ORDER_CODE} />

</div>

<div className={styles.AppBody}>
Expand Down Expand Up @@ -143,6 +147,22 @@ const Home: NextPage = (props) => {
mint={MINT}
tokens={NUM_SPL_TOKENS}
orderCode={ORDER_CODE}/>

<CancelEscrowSpl business={BUSINESS}
validatorAuthority={VALIDATOR}
mintTokenAccount={MINT}
orderCode={ORDER_CODE} />


<ValidateEscrowSpl
validator={VALIDATOR}
business={BUSINESS}
influencer={INFLUENCER}
percentageFee={0}
orderCode={ORDER_CODE}
targetState={1}
textButton={"Validate Escrow Cancel(Target State 1)"}/>

</div>

</WalletContextProvider >
Expand Down
Loading

0 comments on commit 6fda19e

Please sign in to comment.