diff --git a/README.md b/README.md
index 7f75a4e..41d250c 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
# Intoduction
**candy-machine-ui** is a fork of [Fulgurus/candy-machine-v2-responsive-ui](https://github.com/pandao/editor.md "link") repo.
+**Up-to-date with metaplex from v1.1.**
## Features
- **Auto refresh.** Every few seconds ui is updated, This allows you to track the amount of minted NFTs in real time.
diff --git a/public/favicon.ico b/public/favicon.ico
index 88837e8..e31463a 100644
Binary files a/public/favicon.ico and b/public/favicon.ico differ
diff --git a/public/index.html b/public/index.html
index a6d4d49..8abd9ac 100644
--- a/public/index.html
+++ b/public/index.html
@@ -7,7 +7,7 @@
-
Forest Industry
+ Candy Machine UI
You need to enable JavaScript to run this app.
diff --git a/public/logo192.png b/public/logo192.png
index 4f81aea..00e0d55 100644
Binary files a/public/logo192.png and b/public/logo192.png differ
diff --git a/public/logo512.png b/public/logo512.png
index 95ae3cc..0e277be 100644
Binary files a/public/logo512.png and b/public/logo512.png differ
diff --git a/src/App.tsx b/src/App.tsx
index f6a590f..d463bd5 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -23,27 +23,36 @@ import {
} from '@solana/wallet-adapter-react-ui';
import "./App.css";
+import { DEFAULT_TIMEOUT } from './connection';
import {MintPage} from "./Home";
require('@solana/wallet-adapter-react-ui/styles.css');
+const getCandyMachineId = (): anchor.web3.PublicKey | undefined => {
+ try {
+ const candyMachineId = new anchor.web3.PublicKey(
+ process.env.REACT_APP_CANDY_MACHINE_ID!,
+ );
-const candyMachineId = new anchor.web3.PublicKey(
- process.env.REACT_APP_CANDY_MACHINE_ID!
-);
+ return candyMachineId;
+ } catch (e) {
+ console.log('Failed to construct CandyMachineId', e);
+ return undefined;
+ }
+};
+
+const candyMachineId = getCandyMachineId();
const network = process.env.REACT_APP_SOLANA_NETWORK as WalletAdapterNetwork;
const rpcHost = process.env.REACT_APP_SOLANA_RPC_HOST!;
-const connection = new anchor.web3.Connection(rpcHost);
-
-const txTimeout = 30000; // milliseconds (confirm this works for your project)
-
-
+const connection = new anchor.web3.Connection(
+ rpcHost ? rpcHost : anchor.web3.clusterApiUrl('devnet'),
+);
const App = () => {
// Custom RPC endpoint.
- const endpoint = useMemo(() => clusterApiUrl(network), []);
+ const endpoint = useMemo(() => clusterApiUrl(network), []);
// @solana/wallet-adapter-wallets includes all the adapters but supports tree shaking and lazy loading --
// Only the wallets you configure here will be compiled into your application, and only the dependencies
@@ -63,21 +72,21 @@ const App = () => {
[]
);
- return (
-
+ return (
-
-
-
-
-
+
+
+
+
+
- );
+ );
};
export default App;
diff --git a/src/Home.tsx b/src/Home.tsx
index 43ea863..9b53ebc 100644
--- a/src/Home.tsx
+++ b/src/Home.tsx
@@ -1,29 +1,36 @@
-import React, {useEffect, useState} from "react";
+import {useCallback, useEffect, useMemo, useState} from "react";
+import Countdown from "react-countdown";
+import {Snackbar, Alert, Paper, Grid, useTheme, Container, Typography} from "@mui/material";
+
import confetti from "canvas-confetti";
import * as anchor from "@project-serum/anchor";
-import {LAMPORTS_PER_SOL, PublicKey} from "@solana/web3.js";
-import {useAnchorWallet} from "@solana/wallet-adapter-react";
+
+import {
+ Commitment,
+ Connection,
+ PublicKey,
+ Transaction,
+ LAMPORTS_PER_SOL
+} from "@solana/web3.js";
+import {WalletAdapterNetwork} from '@solana/wallet-adapter-base';
+import {useWallet} from "@solana/wallet-adapter-react";
+import {WalletMultiButton} from "@solana/wallet-adapter-react-ui";
import {GatewayProvider} from '@civic/solana-gateway-react';
-import Countdown from "react-countdown";
-import {Snackbar, Alert, Paper, Grid, useTheme, Container, Typography} from "@mui/material";
-import {toDate, AlertState, getAtaForMint} from './utils';
-import MintButton from './components/MintButton';
-import './Home.css'
-import {MainContainer,
- ConnectButton,
- FullWidthConnectButton,
- Wallet,
- WalletAmount,
- WalletContainer,
-} from "./components/styled";
+
+import {AlertState, getAtaForMint, toDate} from './utils';
import {
- CandyMachine,
awaitTransactionSignatureConfirmation,
+ CANDY_MACHINE_PROGRAM,
+ CandyMachineAccount,
+ createAccountsForMint,
getCandyMachineState,
+ getCollectionPDA,
mintOneToken,
- CANDY_MACHINE_PROGRAM,
+ SetupState,
} from "./candy-machine";
+
+
import nft_image from "./img/nft_image.gif"
@@ -31,25 +38,39 @@ import Info from "./components/Info";
import CountDown from "./components/Countdown";
import InactiveMintButton from "./components/InactiveMintButton";
import ProgressBar from "./components/ProgressBar";
+import MintButton from './components/MintButton';
+
+import './Home.css'
+import {MainContainer,
+ ConnectButton,
+ FullWidthConnectButton,
+ Wallet,
+ WalletAmount,
+ WalletContainer,
+} from "./components/styled";
import isMobile from "./components/isMobile"
import {startDate, startWlDate, mintPrice, supply} from "./constants";
+const cluster = process.env.REACT_APP_SOLANA_NETWORK!.toString();
const decimals = process.env.REACT_APP_SPL_TOKEN_TO_MINT_DECIMALS ? +process.env.REACT_APP_SPL_TOKEN_TO_MINT_DECIMALS!.toString() : 9;
const splTokenName = process.env.REACT_APP_SPL_TOKEN_TO_MINT_NAME ? process.env.REACT_APP_SPL_TOKEN_TO_MINT_NAME.toString() : "TOKEN";
+
export interface HomeProps {
- candyMachineId: anchor.web3.PublicKey;
+ candyMachineId?: anchor.web3.PublicKey;
connection: anchor.web3.Connection;
txTimeout: number;
rpcHost: string;
+ network: WalletAdapterNetwork;
}
export const MintPage = (props: HomeProps) => {
const [balance, setBalance] = useState();
const [isMinting, setIsMinting] = useState(false); // true when user got to press MINT
const [isActive, setIsActive] = useState(false); // true when countdown completes or whitelisted
+ const [solanaExplorerLink, setSolanaExplorerLink] = useState("");
const [itemsAvailable, setItemsAvailable] = useState(0);
const [itemsRedeemed, setItemsRedeemed] = useState(0);
const [itemsRemaining, setItemsRemaining] = useState(0);
@@ -65,10 +86,6 @@ export const MintPage = (props: HomeProps) => {
const [endDate, setEndDate] = useState();
const [isPresale, setIsPresale] = useState(false);
const [isWLOnly, setIsWLOnly] = useState(false);
- const [refreshFlag, setRefreshFlag] = useState(false)
-
- const mobileMarker = isMobile()
- const theme = useTheme()
const [alertState, setAlertState] = useState({
open: false,
@@ -76,137 +93,223 @@ export const MintPage = (props: HomeProps) => {
severity: undefined,
});
- const wallet = useAnchorWallet();
- const [candyMachine, setCandyMachine] = useState();
+ const [needTxnSplit, setNeedTxnSplit] = useState(true);
+ const [setupTxn, setSetupTxn] = useState();
+
+ const wallet = useWallet();
+ const [candyMachine, setCandyMachine] = useState();
const rpcUrl = props.rpcHost;
+ const solFeesEstimation = 0.012; // approx of account creation fees
+
+ const anchorWallet = useMemo(() => {
+ if (
+ !wallet ||
+ !wallet.publicKey ||
+ !wallet.signAllTransactions ||
+ !wallet.signTransaction
+ ) {
+ return;
+ }
- const refreshCandyMachineState = () => {
- (async () => {
- if (!wallet) return;
-
- const cndy = await getCandyMachineState(
- wallet as anchor.Wallet,
- props.candyMachineId,
- props.connection
- );
- setCandyMachine(cndy);
- setItemsAvailable(cndy.state.itemsAvailable);
- setItemsRemaining(cndy.state.itemsRemaining);
- let tempItems = itemsRedeemed
- setItemsRedeemed(cndy.state.itemsRedeemed >= tempItems ? cndy.state.itemsRedeemed : tempItems);
-
- var divider = 1;
- if (decimals) {
- divider = +('1' + new Array(decimals).join('0').slice() + '0');
+ return {
+ publicKey: wallet.publicKey,
+ signAllTransactions: wallet.signAllTransactions,
+ signTransaction: wallet.signTransaction,
+ } as anchor.Wallet;
+ }, [wallet]);
+
+ const refreshCandyMachineState = useCallback(
+ async (commitment: Commitment = 'confirmed') => {
+ if (!anchorWallet) {
+ return;
}
- // detect if using spl-token to mint
- if (cndy.state.tokenMint) {
- setPayWithSplToken(true);
- // Customize your SPL-TOKEN Label HERE
- // TODO: get spl-token metadata name
- setPriceLabel(splTokenName);
- setPrice(cndy.state.price.toNumber() / divider);
- setWhitelistPrice(cndy.state.price.toNumber() / divider);
- }else {
- setPrice(cndy.state.price.toNumber() / LAMPORTS_PER_SOL);
- setWhitelistPrice(cndy.state.price.toNumber() / LAMPORTS_PER_SOL);
- }
+ const connection = new Connection(props.rpcHost, commitment);
+ if (props.candyMachineId) {
+ try {
+ const cndy = await getCandyMachineState(
+ anchorWallet,
+ props.candyMachineId,
+ connection,
+ );
+
+ setCandyMachine(cndy);
+ setItemsAvailable(cndy.state.itemsAvailable);
+ setItemsRemaining(cndy.state.itemsRemaining);
+ setItemsRedeemed(cndy.state.itemsRedeemed);
- // fetch whitelist token balance
- if (cndy.state.whitelistMintSettings) {
- setWhitelistEnabled(true);
- setIsBurnToken(cndy.state.whitelistMintSettings.mode.burnEveryTime);
- setIsPresale(cndy.state.whitelistMintSettings.presale);
- setIsWLOnly(!isPresale && cndy.state.whitelistMintSettings.discountPrice === null);
+ var divider = 1;
+ if (decimals) {
+ divider = +('1' + new Array(decimals).join('0').slice() + '0');
+ }
- if (cndy.state.whitelistMintSettings.discountPrice !== null && cndy.state.whitelistMintSettings.discountPrice !== cndy.state.price) {
+ // detect if using spl-token to mint
if (cndy.state.tokenMint) {
- setWhitelistPrice(cndy.state.whitelistMintSettings.discountPrice?.toNumber() / divider);
+ setPayWithSplToken(true);
+ // Customize your SPL-TOKEN Label HERE
+ // TODO: get spl-token metadata name
+ setPriceLabel(splTokenName);
+ setPrice(cndy.state.price.toNumber() / divider);
+ setWhitelistPrice(cndy.state.price.toNumber() / divider);
} else {
- setWhitelistPrice(cndy.state.whitelistMintSettings.discountPrice?.toNumber() / LAMPORTS_PER_SOL);
+ setPrice(cndy.state.price.toNumber() / LAMPORTS_PER_SOL);
+ setWhitelistPrice(cndy.state.price.toNumber() / LAMPORTS_PER_SOL);
}
- }
- let balance = 0;
- try {
- const tokenBalance =
- await props.connection.getTokenAccountBalance(
- (
- await getAtaForMint(
- cndy.state.whitelistMintSettings.mint,
- wallet.publicKey,
- )
- )[0],
+
+ // fetch whitelist token balance
+ if (cndy.state.whitelistMintSettings) {
+ setWhitelistEnabled(true);
+ setIsBurnToken(cndy.state.whitelistMintSettings.mode.burnEveryTime);
+ setIsPresale(cndy.state.whitelistMintSettings.presale);
+ setIsWLOnly(!isPresale && cndy.state.whitelistMintSettings.discountPrice === null);
+
+ if (cndy.state.whitelistMintSettings.discountPrice !== null && cndy.state.whitelistMintSettings.discountPrice !== cndy.state.price) {
+ if (cndy.state.tokenMint) {
+ setWhitelistPrice(cndy.state.whitelistMintSettings.discountPrice?.toNumber() / divider);
+ } else {
+ setWhitelistPrice(cndy.state.whitelistMintSettings.discountPrice?.toNumber() / LAMPORTS_PER_SOL);
+ }
+ }
+
+ let balance = 0;
+ try {
+ const tokenBalance =
+ await props.connection.getTokenAccountBalance(
+ (
+ await getAtaForMint(
+ cndy.state.whitelistMintSettings.mint,
+ anchorWallet.publicKey,
+ )
+ )[0],
+ );
+
+ balance = tokenBalance?.value?.uiAmount || 0;
+ } catch (e) {
+ console.error(e);
+ balance = 0;
+ }
+ if (commitment !== "processed") {
+ setWhitelistTokenBalance(balance);
+ }
+ setIsActive(isPresale && !isEnded && balance > 0);
+
+ } else {
+ setWhitelistEnabled(false);
+ }
+
+ // end the mint when date is reached
+ if (cndy?.state.endSettings?.endSettingType.date) {
+ setEndDate(toDate(cndy.state.endSettings.number));
+ if (
+ cndy.state.endSettings.number.toNumber() <
+ new Date().getTime() / 1000
+ ) {
+ setIsEnded(true);
+ setIsActive(false);
+ }
+ }
+ // end the mint when amount is reached
+ if (cndy?.state.endSettings?.endSettingType.amount) {
+ let limit = Math.min(
+ cndy.state.endSettings.number.toNumber(),
+ cndy.state.itemsAvailable,
);
+ setItemsAvailable(limit);
+ if (cndy.state.itemsRedeemed < limit) {
+ setItemsRemaining(limit - cndy.state.itemsRedeemed);
+ } else {
+ setItemsRemaining(0);
+ cndy.state.isSoldOut = true;
+ setIsEnded(true);
+ }
+ } else {
+ setItemsRemaining(cndy.state.itemsRemaining);
+ }
- balance = tokenBalance?.value?.uiAmount || 0;
- } catch (e) {
- console.error(e);
- balance = 0;
- }
- setWhitelistTokenBalance(balance);
- setIsActive(isPresale && !isEnded && balance > 0);
- } else {
- setWhitelistEnabled(false);
- }
+ if (cndy.state.isSoldOut) {
+ setIsActive(false);
+ }
- // end the mint when date is reached
- if (cndy?.state.endSettings?.endSettingType.date) {
- setEndDate(toDate(cndy.state.endSettings.number));
- if (
- cndy.state.endSettings.number.toNumber() <
- new Date().getTime() / 1000
- ) {
- setIsEnded(true);
- setIsActive(false);
- }
- }
- // end the mint when amount is reached
- if (cndy?.state.endSettings?.endSettingType.amount) {
- let limit = Math.min(
- cndy.state.endSettings.number.toNumber(),
- cndy.state.itemsAvailable,
- );
- setItemsAvailable(limit);
- if (cndy.state.itemsRedeemed < limit) {
- setItemsRemaining(limit - cndy.state.itemsRedeemed);
- } else {
- setItemsRemaining(0);
- cndy.state.isSoldOut = true;
- setIsEnded(true);
+ const [collectionPDA] = await getCollectionPDA(props.candyMachineId);
+ const collectionPDAAccount = await connection.getAccountInfo(
+ collectionPDA,
+ );
+
+ const txnEstimate =
+ 892 +
+ (!!collectionPDAAccount && cndy.state.retainAuthority ? 182 : 0) +
+ (cndy.state.tokenMint ? 66 : 0) +
+ (cndy.state.whitelistMintSettings ? 34 : 0) +
+ (cndy.state.whitelistMintSettings?.mode?.burnEveryTime ? 34 : 0) +
+ (cndy.state.gatekeeper ? 33 : 0) +
+ (cndy.state.gatekeeper?.expireOnUse ? 66 : 0);
+
+ setNeedTxnSplit(txnEstimate > 1230);
+ } catch (e) {
+ if (e instanceof Error) {
+ if (
+ e.message === `Account does not exist ${props.candyMachineId}`
+ ) {
+ setAlertState({
+ open: true,
+ message: `Couldn't fetch candy machine state from candy machine with address: ${props.candyMachineId}, using rpc: ${props.rpcHost}! You probably typed the REACT_APP_CANDY_MACHINE_ID value in wrong in your .env file, or you are using the wrong RPC!`,
+ severity: 'error',
+ hideDuration: null,
+ });
+ } else if (
+ e.message.startsWith('failed to get info about account')
+ ) {
+ setAlertState({
+ open: true,
+ message: `Couldn't fetch candy machine state with rpc: ${props.rpcHost}! This probably means you have an issue with the REACT_APP_SOLANA_RPC_HOST value in your .env file, or you are not using a custom RPC!`,
+ severity: 'error',
+ hideDuration: null,
+ });
+ }
+ } else {
+ setAlertState({
+ open: true,
+ message: `${e}`,
+ severity: 'error',
+ hideDuration: null,
+ });
+ }
+ console.log(e);
}
} else {
- setItemsRemaining(cndy.state.itemsRemaining);
+ setAlertState({
+ open: true,
+ message: `Your REACT_APP_CANDY_MACHINE_ID value in the .env file doesn't look right! Make sure you enter it in as plain base-58 address!`,
+ severity: 'error',
+ hideDuration: null,
+ });
}
-
- if (cndy.state.isSoldOut) {
- setIsActive(false);
- }
- })();
- };
-
- const renderGoLiveDateCounter = ({days, hours, minutes, seconds}: any) => {
- return
- };
+ },
+ [anchorWallet, props.candyMachineId, props.rpcHost, isEnded, isPresale, props.connection],
+ );
- function displaySuccess(): void {
- let remaining = itemsRemaining - 1;
+ function displaySuccess(mintPublicKey: any, qty: number = 1): void {
+ let remaining = itemsRemaining - qty;
setItemsRemaining(remaining);
setIsSoldOut(remaining === 0);
if (isBurnToken && whitelistTokenBalance && whitelistTokenBalance > 0) {
- let balance = whitelistTokenBalance - 1;
+ let balance = whitelistTokenBalance - qty;
setWhitelistTokenBalance(balance);
setIsActive(isPresale && !isEnded && balance > 0);
}
- setItemsRedeemed(itemsRedeemed + 1);
- const solFeesEstimation = 0.012; // approx
+ setSetupTxn(undefined);
+ setItemsRedeemed(itemsRedeemed + qty);
if (!payWithSplToken && balance && balance > 0) {
- setBalance(balance - (whitelistEnabled ? whitelistPrice : price) - solFeesEstimation);
+ setBalance(balance - ((whitelistEnabled ? whitelistPrice : price) * qty) - solFeesEstimation);
}
+ setSolanaExplorerLink(cluster === "devnet" || cluster === "testnet"
+ ? ("https://solscan.io/token/" + mintPublicKey + "?cluster=" + cluster)
+ : ("https://solscan.io/token/" + mintPublicKey));
+ setIsMinting(false);
throwConfetti();
};
@@ -218,27 +321,81 @@ export const MintPage = (props: HomeProps) => {
});
}
- const onMint = async () => {
+ const onMint = async (
+ beforeTransactions: Transaction[] = [],
+ afterTransactions: Transaction[] = [],
+ ) => {
try {
- setIsMinting(true);
- if (wallet && candyMachine?.program && wallet.publicKey) {
- const mint = anchor.web3.Keypair.generate();
- const mintTxId = (
- await mintOneToken(candyMachine, wallet.publicKey, mint)
- )[0];
+ if (wallet.connected && candyMachine?.program && wallet.publicKey) {
+ setIsMinting(true);
+ let setupMint: SetupState | undefined;
+ if (needTxnSplit && setupTxn === undefined) {
+ setAlertState({
+ open: true,
+ message: 'Please validate account setup transaction',
+ severity: 'info',
+ });
+ setupMint = await createAccountsForMint(
+ candyMachine,
+ wallet.publicKey,
+ );
+ let status: any = {err: true};
+ if (setupMint.transaction) {
+ status = await awaitTransactionSignatureConfirmation(
+ setupMint.transaction,
+ props.txTimeout,
+ props.connection,
+ true,
+ );
+ }
+ if (status && !status.err) {
+ setSetupTxn(setupMint);
+ setAlertState({
+ open: true,
+ message:
+ 'Setup transaction succeeded! You can now validate mint transaction',
+ severity: 'info',
+ });
+ } else {
+ setAlertState({
+ open: true,
+ message: 'Mint failed! Please try again!',
+ severity: 'error',
+ });
+ return;
+ }
+ }
+
+ const setupState = setupMint ?? setupTxn;
+ const mint = setupState?.mint ?? anchor.web3.Keypair.generate();
+ let mintResult = await mintOneToken(
+ candyMachine,
+ wallet.publicKey,
+ mint,
+ beforeTransactions,
+ afterTransactions,
+ setupState,
+ );
let status: any = {err: true};
- if (mintTxId) {
+ let metadataStatus = null;
+ if (mintResult) {
status = await awaitTransactionSignatureConfirmation(
- mintTxId,
+ mintResult.mintTxId,
props.txTimeout,
props.connection,
- 'singleGossip',
true,
);
+
+ metadataStatus =
+ await candyMachine.program.provider.connection.getAccountInfo(
+ mintResult.metadataKey,
+ 'processed',
+ );
+ console.log('Metadata status: ', !!metadataStatus);
}
- if (!status?.err) {
+ if (status && !status.err && metadataStatus) {
setAlertState({
open: true,
message: 'Congratulations! Mint succeeded!',
@@ -246,17 +403,27 @@ export const MintPage = (props: HomeProps) => {
});
// update front-end amounts
- displaySuccess();
+ displaySuccess(mint.publicKey);
+ refreshCandyMachineState('processed');
+ } else if (status && !status.err) {
+ setAlertState({
+ open: true,
+ message:
+ 'Mint likely failed! Anti-bot SOL 0.01 fee potentially charged! Check the explorer to confirm the mint failed and if so, make sure you are eligible to mint before trying again.',
+ severity: 'error',
+ hideDuration: 8000,
+ });
+ refreshCandyMachineState();
} else {
setAlertState({
open: true,
message: 'Mint failed! Please try again!',
severity: 'error',
});
+ refreshCandyMachineState();
}
}
} catch (error: any) {
- // TODO: blech:
let message = error.msg || 'Minting failed! Please try again!';
if (!error.msg) {
if (!error.message) {
@@ -285,35 +452,34 @@ export const MintPage = (props: HomeProps) => {
}
};
-
useEffect(() => {
(async () => {
- if (wallet) {
- const balance = await props.connection.getBalance(wallet.publicKey);
+ if (anchorWallet) {
+ const balance = await props.connection.getBalance(anchorWallet!.publicKey);
setBalance(balance / LAMPORTS_PER_SOL);
}
})();
- }, [wallet, props.connection]);
+ }, [anchorWallet, props.connection]);
- useEffect(refreshCandyMachineState, [
- wallet,
+ useEffect(() => {
+ refreshCandyMachineState();
+ }, [
+ anchorWallet,
props.candyMachineId,
props.connection,
isEnded,
isPresale,
- refreshFlag
+ refreshCandyMachineState
]);
- useEffect(
- () => {
- let timer1 = setInterval(() => setRefreshFlag(!refreshFlag), 5000);
- return () => {
- clearInterval(timer1);
- };
- },
- [refreshFlag]
- );
+
+ const mobileMarker = isMobile()
+ const theme = useTheme()
+
+ const renderGoLiveDateCounter = ({days, hours, minutes, seconds}: any) => {
+ return
+ };
diff --git a/src/candy-machine.ts b/src/candy-machine.ts
index 563e401..4785666 100644
--- a/src/candy-machine.ts
+++ b/src/candy-machine.ts
@@ -1,13 +1,12 @@
-import * as anchor from "@project-serum/anchor";
+import * as anchor from '@project-serum/anchor';
+import { MintLayout, TOKEN_PROGRAM_ID, Token } from '@solana/spl-token';
import {
- MintLayout,
- TOKEN_PROGRAM_ID,
- Token,
-} from "@solana/spl-token";
-
-import { SystemProgram } from '@solana/web3.js';
-import { sendTransactions } from './connection';
+ SystemProgram,
+ Transaction,
+ SYSVAR_SLOT_HASHES_PUBKEY,
+} from '@solana/web3.js';
+import { sendTransactions, SequenceType } from './connection';
import {
CIVIC,
@@ -18,27 +17,24 @@ import {
} from './utils';
export const CANDY_MACHINE_PROGRAM = new anchor.web3.PublicKey(
- "cndy3Z4yapfJBmL3ShUp5exZKqR3z33thTzeNMm2gRZ"
+ 'cndy3Z4yapfJBmL3ShUp5exZKqR3z33thTzeNMm2gRZ',
);
const TOKEN_METADATA_PROGRAM_ID = new anchor.web3.PublicKey(
- "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
+ 'metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s',
);
-export interface CandyMachine {
- id: anchor.web3.PublicKey,
- program: anchor.Program;
- state: CandyMachineState;
-}
-
interface CandyMachineState {
+ authority: anchor.web3.PublicKey;
itemsAvailable: number;
itemsRedeemed: number;
itemsRemaining: number;
treasury: anchor.web3.PublicKey;
- tokenMint: anchor.web3.PublicKey;
+ tokenMint: null | anchor.web3.PublicKey;
isSoldOut: boolean;
isActive: boolean;
+ isPresale: boolean;
+ isWhitelistOnly: boolean;
goLiveDate: anchor.BN;
price: anchor.BN;
gatekeeper: null | {
@@ -60,14 +56,20 @@ interface CandyMachineState {
uri: string;
hash: Uint8Array;
};
+ retainAuthority: boolean;
+}
+
+export interface CandyMachineAccount {
+ id: anchor.web3.PublicKey;
+ program: anchor.Program;
+ state: CandyMachineState;
}
export const awaitTransactionSignatureConfirmation = async (
- txid: anchor.web3.TransactionSignature,
- timeout: number,
- connection: anchor.web3.Connection,
- commitment: anchor.web3.Commitment = 'recent',
- queryStatus = false,
+ txid: anchor.web3.TransactionSignature,
+ timeout: number,
+ connection: anchor.web3.Connection,
+ queryStatus = false,
): Promise => {
let done = false;
let status: anchor.web3.SignatureStatus | null | void = {
@@ -85,6 +87,7 @@ export const awaitTransactionSignatureConfirmation = async (
console.log('Rejecting for timeout...');
reject({ timeout: true });
}, timeout);
+
while (!done && queryStatus) {
// eslint-disable-next-line no-loop-func
(async () => {
@@ -118,20 +121,16 @@ export const awaitTransactionSignatureConfirmation = async (
}
});
- //@ts-ignore
- if (connection._signatureSubscriptions[subId]) {
- connection.removeSignatureListener(subId);
- }
done = true;
console.log('Returning status', status);
return status;
};
-/* export */ const createAssociatedTokenAccountInstruction = (
- associatedTokenAddress: anchor.web3.PublicKey,
- payer: anchor.web3.PublicKey,
- walletAddress: anchor.web3.PublicKey,
- splTokenMintAddress: anchor.web3.PublicKey,
+const createAssociatedTokenAccountInstruction = (
+ associatedTokenAddress: anchor.web3.PublicKey,
+ payer: anchor.web3.PublicKey,
+ walletAddress: anchor.web3.PublicKey,
+ splTokenMintAddress: anchor.web3.PublicKey,
) => {
const keys = [
{ pubkey: payer, isSigner: true, isWritable: true },
@@ -158,17 +157,17 @@ export const awaitTransactionSignatureConfirmation = async (
};
export const getCandyMachineState = async (
- anchorWallet: anchor.Wallet,
- candyMachineId: anchor.web3.PublicKey,
- connection: anchor.web3.Connection,
-): Promise => {
+ anchorWallet: anchor.Wallet,
+ candyMachineId: anchor.web3.PublicKey,
+ connection: anchor.web3.Connection,
+): Promise => {
const provider = new anchor.Provider(connection, anchorWallet, {
- preflightCommitment: 'recent',
+ preflightCommitment: 'processed',
});
const idl = await anchor.Program.fetchIdl(CANDY_MACHINE_PROGRAM, provider);
- const program = new anchor.Program(idl, CANDY_MACHINE_PROGRAM, provider);
+ const program = new anchor.Program(idl!, CANDY_MACHINE_PROGRAM, provider);
const state: any = await program.account.candyMachine.fetch(candyMachineId);
const itemsAvailable = state.data.itemsAvailable.toNumber();
@@ -179,18 +178,14 @@ export const getCandyMachineState = async (
id: candyMachineId,
program,
state: {
+ authority: state.authority,
itemsAvailable,
itemsRedeemed,
itemsRemaining,
isSoldOut: itemsRemaining === 0,
- isActive:
- state.data.goLiveDate &&
- state.data.goLiveDate.toNumber() < new Date().getTime() / 1000 &&
- (state.endSettings
- ? state.endSettings.endSettingType.date
- ? state.endSettings.number.toNumber() > new Date().getTime() / 1000
- : itemsRedeemed < state.endSettings.number.toNumber()
- : true),
+ isActive: false,
+ isPresale: false,
+ isWhitelistOnly: false,
goLiveDate: state.data.goLiveDate,
treasury: state.wallet,
tokenMint: state.tokenMint,
@@ -199,112 +194,231 @@ export const getCandyMachineState = async (
whitelistMintSettings: state.data.whitelistMintSettings,
hiddenSettings: state.data.hiddenSettings,
price: state.data.price,
+ retainAuthority: state.data.retainAuthority,
},
};
};
const getMasterEdition = async (
- mint: anchor.web3.PublicKey,
+ mint: anchor.web3.PublicKey,
): Promise => {
return (
- await anchor.web3.PublicKey.findProgramAddress(
- [
- Buffer.from('metadata'),
- TOKEN_METADATA_PROGRAM_ID.toBuffer(),
- mint.toBuffer(),
- Buffer.from('edition'),
- ],
- TOKEN_METADATA_PROGRAM_ID,
- )
+ await anchor.web3.PublicKey.findProgramAddress(
+ [
+ Buffer.from('metadata'),
+ TOKEN_METADATA_PROGRAM_ID.toBuffer(),
+ mint.toBuffer(),
+ Buffer.from('edition'),
+ ],
+ TOKEN_METADATA_PROGRAM_ID,
+ )
)[0];
};
const getMetadata = async (
- mint: anchor.web3.PublicKey,
+ mint: anchor.web3.PublicKey,
): Promise => {
return (
- await anchor.web3.PublicKey.findProgramAddress(
- [
- Buffer.from('metadata'),
- TOKEN_METADATA_PROGRAM_ID.toBuffer(),
- mint.toBuffer(),
- ],
- TOKEN_METADATA_PROGRAM_ID,
- )
+ await anchor.web3.PublicKey.findProgramAddress(
+ [
+ Buffer.from('metadata'),
+ TOKEN_METADATA_PROGRAM_ID.toBuffer(),
+ mint.toBuffer(),
+ ],
+ TOKEN_METADATA_PROGRAM_ID,
+ )
)[0];
};
export const getCandyMachineCreator = async (
- candyMachine: anchor.web3.PublicKey,
+ candyMachine: anchor.web3.PublicKey,
): Promise<[anchor.web3.PublicKey, number]> => {
return await anchor.web3.PublicKey.findProgramAddress(
- [Buffer.from('candy_machine'), candyMachine.toBuffer()],
- CANDY_MACHINE_PROGRAM,
+ [Buffer.from('candy_machine'), candyMachine.toBuffer()],
+ CANDY_MACHINE_PROGRAM,
);
};
-export const mintOneToken = async (
- candyMachine: CandyMachine,
+export const getCollectionPDA = async (
+ candyMachineAddress: anchor.web3.PublicKey,
+): Promise<[anchor.web3.PublicKey, number]> => {
+ return await anchor.web3.PublicKey.findProgramAddress(
+ [Buffer.from('collection'), candyMachineAddress.toBuffer()],
+ CANDY_MACHINE_PROGRAM,
+ );
+};
+
+export interface CollectionData {
+ mint: anchor.web3.PublicKey;
+ candyMachine: anchor.web3.PublicKey;
+}
+
+export const getCollectionAuthorityRecordPDA = async (
+ mint: anchor.web3.PublicKey,
+ newAuthority: anchor.web3.PublicKey,
+): Promise => {
+ return (
+ await anchor.web3.PublicKey.findProgramAddress(
+ [
+ Buffer.from('metadata'),
+ TOKEN_METADATA_PROGRAM_ID.toBuffer(),
+ mint.toBuffer(),
+ Buffer.from('collection_authority'),
+ newAuthority.toBuffer(),
+ ],
+ TOKEN_METADATA_PROGRAM_ID,
+ )
+ )[0];
+};
+
+export type SetupState = {
+ mint: anchor.web3.Keypair;
+ userTokenAccount: anchor.web3.PublicKey;
+ transaction: string;
+};
+
+export const createAccountsForMint = async (
+ candyMachine: CandyMachineAccount,
payer: anchor.web3.PublicKey,
- mint: anchor.web3.Keypair
-): Promise<(string | undefined)[]> => {
+): Promise => {
+ const mint = anchor.web3.Keypair.generate();
const userTokenAccountAddress = (
- await getAtaForMint(mint.publicKey, payer)
+ await getAtaForMint(mint.publicKey, payer)
)[0];
- const userPayingAccountAddress = candyMachine.state.tokenMint
- ? (await getAtaForMint(candyMachine.state.tokenMint, payer))[0]
- : payer;
-
- const candyMachineAddress = candyMachine.id;
- const remainingAccounts = [];
const signers: anchor.web3.Keypair[] = [mint];
- const cleanupInstructions = [];
const instructions = [
anchor.web3.SystemProgram.createAccount({
fromPubkey: payer,
newAccountPubkey: mint.publicKey,
space: MintLayout.span,
lamports:
- await candyMachine.program.provider.connection.getMinimumBalanceForRentExemption(
- MintLayout.span,
- ),
+ await candyMachine.program.provider.connection.getMinimumBalanceForRentExemption(
+ MintLayout.span,
+ ),
programId: TOKEN_PROGRAM_ID,
}),
Token.createInitMintInstruction(
- TOKEN_PROGRAM_ID,
- mint.publicKey,
- 0,
- payer,
- payer,
+ TOKEN_PROGRAM_ID,
+ mint.publicKey,
+ 0,
+ payer,
+ payer,
),
createAssociatedTokenAccountInstruction(
- userTokenAccountAddress,
- payer,
- payer,
- mint.publicKey,
+ userTokenAccountAddress,
+ payer,
+ payer,
+ mint.publicKey,
),
Token.createMintToInstruction(
- TOKEN_PROGRAM_ID,
- mint.publicKey,
- userTokenAccountAddress,
- payer,
- [],
- 1,
+ TOKEN_PROGRAM_ID,
+ mint.publicKey,
+ userTokenAccountAddress,
+ payer,
+ [],
+ 1,
),
];
+ return {
+ mint: mint,
+ userTokenAccount: userTokenAccountAddress,
+ transaction: (
+ await sendTransactions(
+ candyMachine.program.provider.connection,
+ candyMachine.program.provider.wallet,
+ [instructions],
+ [signers],
+ SequenceType.StopOnFailure,
+ 'singleGossip',
+ () => {},
+ () => false,
+ undefined,
+ [],
+ [],
+ )
+ ).txs[0].txid,
+ };
+};
+
+type MintResult = {
+ mintTxId: string;
+ metadataKey: anchor.web3.PublicKey;
+};
+
+export const mintOneToken = async (
+ candyMachine: CandyMachineAccount,
+ payer: anchor.web3.PublicKey,
+ mint: anchor.web3.Keypair,
+ beforeTransactions: Transaction[] = [],
+ afterTransactions: Transaction[] = [],
+ setupState?: SetupState,
+): Promise => {
+ const userTokenAccountAddress = (
+ await getAtaForMint(mint.publicKey, payer)
+ )[0];
+
+ const userPayingAccountAddress = candyMachine.state.tokenMint
+ ? (await getAtaForMint(candyMachine.state.tokenMint, payer))[0]
+ : payer;
+
+ const candyMachineAddress = candyMachine.id;
+ const remainingAccounts = [];
+ const instructions = [];
+ const signers: anchor.web3.Keypair[] = [];
+ console.log('SetupState: ', setupState);
+ if (!setupState) {
+ signers.push(mint);
+ instructions.push(
+ ...[
+ anchor.web3.SystemProgram.createAccount({
+ fromPubkey: payer,
+ newAccountPubkey: mint.publicKey,
+ space: MintLayout.span,
+ lamports:
+ await candyMachine.program.provider.connection.getMinimumBalanceForRentExemption(
+ MintLayout.span,
+ ),
+ programId: TOKEN_PROGRAM_ID,
+ }),
+ Token.createInitMintInstruction(
+ TOKEN_PROGRAM_ID,
+ mint.publicKey,
+ 0,
+ payer,
+ payer,
+ ),
+ createAssociatedTokenAccountInstruction(
+ userTokenAccountAddress,
+ payer,
+ payer,
+ mint.publicKey,
+ ),
+ Token.createMintToInstruction(
+ TOKEN_PROGRAM_ID,
+ mint.publicKey,
+ userTokenAccountAddress,
+ payer,
+ [],
+ 1,
+ ),
+ ],
+ );
+ }
+
if (candyMachine.state.gatekeeper) {
remainingAccounts.push({
pubkey: (
- await getNetworkToken(
- payer,
- candyMachine.state.gatekeeper.gatekeeperNetwork,
- )
+ await getNetworkToken(
+ payer,
+ candyMachine.state.gatekeeper.gatekeeperNetwork,
+ )
)[0],
isWritable: true,
isSigner: false,
});
+
if (candyMachine.state.gatekeeper.expireOnUse) {
remainingAccounts.push({
pubkey: CIVIC,
@@ -313,9 +427,9 @@ export const mintOneToken = async (
});
remainingAccounts.push({
pubkey: (
- await getNetworkExpire(
- candyMachine.state.gatekeeper.gatekeeperNetwork,
- )
+ await getNetworkExpire(
+ candyMachine.state.gatekeeper.gatekeeperNetwork,
+ )
)[0],
isWritable: false,
isSigner: false,
@@ -324,7 +438,7 @@ export const mintOneToken = async (
}
if (candyMachine.state.whitelistMintSettings) {
const mint = new anchor.web3.PublicKey(
- candyMachine.state.whitelistMintSettings.mint,
+ candyMachine.state.whitelistMintSettings.mint,
);
const whitelistToken = (await getAtaForMint(mint, payer))[0];
@@ -335,126 +449,139 @@ export const mintOneToken = async (
});
if (candyMachine.state.whitelistMintSettings.mode.burnEveryTime) {
- const whitelistBurnAuthority = anchor.web3.Keypair.generate();
-
remainingAccounts.push({
pubkey: mint,
isWritable: true,
isSigner: false,
});
remainingAccounts.push({
- pubkey: whitelistBurnAuthority.publicKey,
+ pubkey: payer,
isWritable: false,
isSigner: true,
});
- signers.push(whitelistBurnAuthority);
- const exists =
- await candyMachine.program.provider.connection.getAccountInfo(
- whitelistToken,
- );
- if (exists) {
- instructions.push(
- Token.createApproveInstruction(
- TOKEN_PROGRAM_ID,
- whitelistToken,
- whitelistBurnAuthority.publicKey,
- payer,
- [],
- 1,
- ),
- );
- cleanupInstructions.push(
- Token.createRevokeInstruction(
- TOKEN_PROGRAM_ID,
- whitelistToken,
- payer,
- [],
- ),
- );
- }
}
}
if (candyMachine.state.tokenMint) {
- const transferAuthority = anchor.web3.Keypair.generate();
-
- signers.push(transferAuthority);
remainingAccounts.push({
pubkey: userPayingAccountAddress,
isWritable: true,
isSigner: false,
});
remainingAccounts.push({
- pubkey: transferAuthority.publicKey,
+ pubkey: payer,
isWritable: false,
isSigner: true,
});
-
- instructions.push(
- Token.createApproveInstruction(
- TOKEN_PROGRAM_ID,
- userPayingAccountAddress,
- transferAuthority.publicKey,
- payer,
- [],
- candyMachine.state.price.toNumber(),
- ),
- );
- cleanupInstructions.push(
- Token.createRevokeInstruction(
- TOKEN_PROGRAM_ID,
- userPayingAccountAddress,
- payer,
- [],
- ),
- );
}
const metadataAddress = await getMetadata(mint.publicKey);
const masterEdition = await getMasterEdition(mint.publicKey);
const [candyMachineCreator, creatorBump] = await getCandyMachineCreator(
- candyMachineAddress,
+ candyMachineAddress,
);
+ console.log(remainingAccounts.map(rm => rm.pubkey.toBase58()));
instructions.push(
- await candyMachine.program.instruction.mintNft(creatorBump, {
- accounts: {
- candyMachine: candyMachineAddress,
- candyMachineCreator,
- payer: payer,
- wallet: candyMachine.state.treasury,
- mint: mint.publicKey,
- metadata: metadataAddress,
- masterEdition,
- mintAuthority: payer,
- updateAuthority: payer,
- tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
- tokenProgram: TOKEN_PROGRAM_ID,
- systemProgram: SystemProgram.programId,
- rent: anchor.web3.SYSVAR_RENT_PUBKEY,
- clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
- recentBlockhashes: anchor.web3.SYSVAR_RECENT_BLOCKHASHES_PUBKEY,
- instructionSysvarAccount: anchor.web3.SYSVAR_INSTRUCTIONS_PUBKEY,
- },
- remainingAccounts:
- remainingAccounts.length > 0 ? remainingAccounts : undefined,
- }),
+ await candyMachine.program.instruction.mintNft(creatorBump, {
+ accounts: {
+ candyMachine: candyMachineAddress,
+ candyMachineCreator,
+ payer: payer,
+ wallet: candyMachine.state.treasury,
+ mint: mint.publicKey,
+ metadata: metadataAddress,
+ masterEdition,
+ mintAuthority: payer,
+ updateAuthority: payer,
+ tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
+ tokenProgram: TOKEN_PROGRAM_ID,
+ systemProgram: SystemProgram.programId,
+ rent: anchor.web3.SYSVAR_RENT_PUBKEY,
+ clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
+ recentBlockhashes: SYSVAR_SLOT_HASHES_PUBKEY,
+ instructionSysvarAccount: anchor.web3.SYSVAR_INSTRUCTIONS_PUBKEY,
+ },
+ remainingAccounts:
+ remainingAccounts.length > 0 ? remainingAccounts : undefined,
+ }),
);
+ const [collectionPDA] = await getCollectionPDA(candyMachineAddress);
+ const collectionPDAAccount =
+ await candyMachine.program.provider.connection.getAccountInfo(
+ collectionPDA,
+ );
+
+ if (collectionPDAAccount && candyMachine.state.retainAuthority) {
+ try {
+ const collectionData =
+ (await candyMachine.program.account.collectionPda.fetch(
+ collectionPDA,
+ )) as CollectionData;
+ console.log(collectionData);
+ const collectionMint = collectionData.mint;
+ const collectionAuthorityRecord = await getCollectionAuthorityRecordPDA(
+ collectionMint,
+ collectionPDA,
+ );
+ console.log(collectionMint);
+ if (collectionMint) {
+ const collectionMetadata = await getMetadata(collectionMint);
+ const collectionMasterEdition = await getMasterEdition(collectionMint);
+ console.log('Collection PDA: ', collectionPDA.toBase58());
+ console.log('Authority: ', candyMachine.state.authority.toBase58());
+ instructions.push(
+ await candyMachine.program.instruction.setCollectionDuringMint({
+ accounts: {
+ candyMachine: candyMachineAddress,
+ metadata: metadataAddress,
+ payer: payer,
+ collectionPda: collectionPDA,
+ tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
+ instructions: anchor.web3.SYSVAR_INSTRUCTIONS_PUBKEY,
+ collectionMint,
+ collectionMetadata,
+ collectionMasterEdition,
+ authority: candyMachine.state.authority,
+ collectionAuthorityRecord,
+ },
+ }),
+ );
+ }
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ const instructionsMatrix = [instructions];
+ const signersMatrix = [signers];
+
try {
- return (
- await sendTransactions(
- candyMachine.program.provider.connection,
- candyMachine.program.provider.wallet,
- [instructions, cleanupInstructions],
- [signers, []],
- )
+ const txns = (
+ await sendTransactions(
+ candyMachine.program.provider.connection,
+ candyMachine.program.provider.wallet,
+ instructionsMatrix,
+ signersMatrix,
+ SequenceType.StopOnFailure,
+ 'singleGossip',
+ () => {},
+ () => false,
+ undefined,
+ beforeTransactions,
+ afterTransactions,
+ )
).txs.map(t => t.txid);
+ const mintTxn = txns[0];
+ return {
+ mintTxId: mintTxn,
+ metadataKey: metadataAddress,
+ };
} catch (e) {
console.log(e);
}
-
- return [];
+ return null;
};
export const shortenAddress = (address: string, chars = 4): string => {
@@ -462,5 +589,5 @@ export const shortenAddress = (address: string, chars = 4): string => {
};
const sleep = (ms: number): Promise => {
- return new Promise((resolve) => setTimeout(resolve, ms));
-}
\ No newline at end of file
+ return new Promise(resolve => setTimeout(resolve, ms));
+};
diff --git a/src/components/MintButton/MintButton.tsx b/src/components/MintButton/MintButton.tsx
index d27fd09..ea81caa 100644
--- a/src/components/MintButton/MintButton.tsx
+++ b/src/components/MintButton/MintButton.tsx
@@ -2,7 +2,7 @@ import styled from 'styled-components';
import {useEffect, useState} from 'react';
import {Button, CircularProgress} from '@mui/material';
import {GatewayStatus, useGateway} from '@civic/solana-gateway-react';
-import {CandyMachine} from '../../candy-machine';
+import {CandyMachineAccount} from '../../candy-machine';
const CTAButton = styled(Button)`
@@ -26,7 +26,7 @@ export const MintButton = ({
isSoldOut
}: {
onMint: () => Promise;
- candyMachine: CandyMachine | undefined;
+ candyMachine?: CandyMachineAccount;
isMinting: boolean;
isEnded: boolean;
isActive: boolean;
diff --git a/src/connection.tsx b/src/connection.tsx
index e2a6b3e..1a534e6 100644
--- a/src/connection.tsx
+++ b/src/connection.tsx
@@ -1,294 +1,303 @@
import {
- Keypair,
- Commitment,
- Connection,
- RpcResponseAndContext,
- SignatureStatus,
- SimulatedTransactionResponse,
- Transaction,
- TransactionInstruction,
- TransactionSignature,
- Blockhash,
- FeeCalculator,
- } from '@solana/web3.js';
-
- import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
-
- interface BlockhashAndFeeCalculator {
- blockhash: Blockhash;
- feeCalculator: FeeCalculator;
- }
-
- export const getErrorForTransaction = async (
+ Keypair,
+ Commitment,
+ Connection,
+ RpcResponseAndContext,
+ SignatureStatus,
+ SimulatedTransactionResponse,
+ Transaction,
+ TransactionInstruction,
+ TransactionSignature,
+ Blockhash,
+ FeeCalculator,
+} from '@solana/web3.js';
+
+import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
+
+interface BlockhashAndFeeCalculator {
+ blockhash: Blockhash;
+ feeCalculator: FeeCalculator;
+}
+
+export const DEFAULT_TIMEOUT = 60000;
+
+export const getErrorForTransaction = async (
connection: Connection,
txid: string,
- ) => {
- // wait for all confirmation before geting transaction
- await connection.confirmTransaction(txid, 'max');
-
- const tx = await connection.getParsedConfirmedTransaction(txid);
-
- const errors: string[] = [];
- if (tx?.meta && tx.meta.logMessages) {
- tx.meta.logMessages.forEach(log => {
- const regex = /Error: (.*)/gm;
- let m;
- while ((m = regex.exec(log)) !== null) {
- // This is necessary to avoid infinite loops with zero-width matches
- if (m.index === regex.lastIndex) {
- regex.lastIndex++;
- }
-
- if (m.length > 1) {
- errors.push(m[1]);
- }
+) => {
+ // wait for all confirmation before geting transaction
+ await connection.confirmTransaction(txid, 'max');
+
+ const tx = await connection.getParsedConfirmedTransaction(txid);
+
+ const errors: string[] = [];
+ if (tx?.meta && tx.meta.logMessages) {
+ tx.meta.logMessages.forEach(log => {
+ const regex = /Error: (.*)/gm;
+ let m;
+ while ((m = regex.exec(log)) !== null) {
+ // This is necessary to avoid infinite loops with zero-width matches
+ if (m.index === regex.lastIndex) {
+ regex.lastIndex++;
}
- });
- }
-
- return errors;
- };
-
- export enum SequenceType {
- Sequential,
- Parallel,
- StopOnFailure,
+
+ if (m.length > 1) {
+ errors.push(m[1]);
+ }
+ }
+ });
}
-
- export async function sendTransactionsWithManualRetry(
+
+ return errors;
+};
+
+export enum SequenceType {
+ Sequential,
+ Parallel,
+ StopOnFailure,
+}
+
+export async function sendTransactionsWithManualRetry(
connection: Connection,
wallet: any,
instructions: TransactionInstruction[][],
signers: Keypair[][],
- ): Promise<(string | undefined)[]> {
- let stopPoint = 0;
- let tries = 0;
- let lastInstructionsLength = null;
- let toRemoveSigners: Record = {};
- instructions = instructions.filter((instr, i) => {
- if (instr.length > 0) {
- return true;
- } else {
- toRemoveSigners[i] = true;
- return false;
- }
- });
- let ids: string[] = [];
- let filteredSigners = signers.filter((_, i) => !toRemoveSigners[i]);
-
- while (stopPoint < instructions.length && tries < 3) {
- instructions = instructions.slice(stopPoint, instructions.length);
- filteredSigners = filteredSigners.slice(stopPoint, filteredSigners.length);
-
- if (instructions.length === lastInstructionsLength) tries = tries + 1;
- else tries = 0;
-
- try {
- if (instructions.length === 1) {
- const id = await sendTransactionWithRetry(
+): Promise<(string | undefined)[]> {
+ let stopPoint = 0;
+ let tries = 0;
+ let lastInstructionsLength = null;
+ let toRemoveSigners: Record = {};
+ instructions = instructions.filter((instr, i) => {
+ if (instr.length > 0) {
+ return true;
+ } else {
+ toRemoveSigners[i] = true;
+ return false;
+ }
+ });
+ let ids: string[] = [];
+ let filteredSigners = signers.filter((_, i) => !toRemoveSigners[i]);
+
+ while (stopPoint < instructions.length && tries < 3) {
+ instructions = instructions.slice(stopPoint, instructions.length);
+ filteredSigners = filteredSigners.slice(stopPoint, filteredSigners.length);
+
+ if (instructions.length === lastInstructionsLength) tries = tries + 1;
+ else tries = 0;
+
+ try {
+ if (instructions.length === 1) {
+ const id = await sendTransactionWithRetry(
connection,
wallet,
instructions[0],
filteredSigners[0],
'single',
- );
- ids.push(id.txid);
- stopPoint = 1;
- } else {
- const { txs } = await sendTransactions(
+ );
+ ids.push(id.txid);
+ stopPoint = 1;
+ } else {
+ const { txs } = await sendTransactions(
connection,
wallet,
instructions,
filteredSigners,
SequenceType.StopOnFailure,
'single',
- );
- ids = ids.concat(txs.map(t => t.txid));
- }
- } catch (e) {
- console.error(e);
+ );
+ ids = ids.concat(txs.map(t => t.txid));
}
- console.log(
+ } catch (e) {
+ console.error(e);
+ }
+ console.log(
'Died on ',
stopPoint,
'retrying from instruction',
instructions[stopPoint],
'instructions length is',
instructions.length,
- );
- lastInstructionsLength = instructions.length;
- }
-
- return ids;
+ );
+ lastInstructionsLength = instructions.length;
}
-
- export const sendTransactions = async (
+
+ return ids;
+}
+
+export const sendTransactions = async (
connection: Connection,
wallet: any,
instructionSet: TransactionInstruction[][],
signersSet: Keypair[][],
sequenceType: SequenceType = SequenceType.Parallel,
commitment: Commitment = 'singleGossip',
- successCallback: (txid: string, ind: number) => void = (txid, ind) => { },
+ successCallback: (txid: string, ind: number) => void = (txid, ind) => {},
failCallback: (reason: string, ind: number) => boolean = (txid, ind) => false,
block?: BlockhashAndFeeCalculator,
- ): Promise<{ number: number; txs: { txid: string; slot: number }[] }> => {
- if (!wallet.publicKey) throw new WalletNotConnectedError();
-
- const unsignedTxns: Transaction[] = [];
-
- if (!block) {
- block = await connection.getRecentBlockhash(commitment);
+ beforeTransactions: Transaction[] = [],
+ afterTransactions: Transaction[] = [],
+): Promise<{ number: number; txs: { txid: string; slot: number }[] }> => {
+ if (!wallet.publicKey) throw new WalletNotConnectedError();
+
+ const unsignedTxns: Transaction[] = beforeTransactions;
+
+ if (!block) {
+ block = await connection.getRecentBlockhash(commitment);
+ }
+
+ for (let i = 0; i < instructionSet.length; i++) {
+ const instructions = instructionSet[i];
+ const signers = signersSet[i];
+
+ if (instructions.length === 0) {
+ continue;
}
-
- for (let i = 0; i < instructionSet.length; i++) {
- const instructions = instructionSet[i];
- const signers = signersSet[i];
-
- if (instructions.length === 0) {
- continue;
- }
-
- let transaction = new Transaction();
- instructions.forEach(instruction => transaction.add(instruction));
- transaction.recentBlockhash = block.blockhash;
- transaction.setSigners(
+
+ let transaction = new Transaction();
+ instructions.forEach(instruction => transaction.add(instruction));
+ transaction.recentBlockhash = block.blockhash;
+ transaction.setSigners(
// fee payed by the wallet owner
wallet.publicKey,
...signers.map(s => s.publicKey),
- );
-
- if (signers.length > 0) {
- transaction.partialSign(...signers);
- }
-
- unsignedTxns.push(transaction);
+ );
+
+ if (signers.length > 0) {
+ transaction.partialSign(...signers);
}
-
- const signedTxns = await wallet.signAllTransactions(unsignedTxns);
-
- const pendingTxns: Promise<{ txid: string; slot: number }>[] = [];
-
- let breakEarlyObject = { breakEarly: false, i: 0 };
- console.log(
+
+ unsignedTxns.push(transaction);
+ }
+ unsignedTxns.push(...afterTransactions);
+
+ const partiallySignedTransactions = unsignedTxns.filter(t =>
+ t.signatures.find(sig => sig.publicKey.equals(wallet.publicKey)),
+ );
+ const fullySignedTransactions = unsignedTxns.filter(
+ t => !t.signatures.find(sig => sig.publicKey.equals(wallet.publicKey)),
+ );
+ let signedTxns = await wallet.signAllTransactions(
+ partiallySignedTransactions,
+ );
+ signedTxns = fullySignedTransactions.concat(signedTxns);
+ const pendingTxns: Promise<{ txid: string; slot: number }>[] = [];
+
+ console.log(
'Signed txns length',
signedTxns.length,
'vs handed in length',
instructionSet.length,
- );
- for (let i = 0; i < signedTxns.length; i++) {
- const signedTxnPromise = sendSignedTransaction({
- connection,
- signedTransaction: signedTxns[i],
- });
-
- signedTxnPromise
- .then(({ txid, slot }) => {
- successCallback(txid, i);
- })
- .catch(reason => {
- // @ts-ignore
- failCallback(signedTxns[i], i);
- if (sequenceType === SequenceType.StopOnFailure) {
- breakEarlyObject.breakEarly = true;
- breakEarlyObject.i = i;
- }
- });
-
- if (sequenceType !== SequenceType.Parallel) {
- try {
- await signedTxnPromise;
- } catch (e) {
- console.log('Caught failure', e);
- if (breakEarlyObject.breakEarly) {
- console.log('Died on ', breakEarlyObject.i);
- // Return the txn we failed on by index
- return {
- number: breakEarlyObject.i,
- txs: await Promise.all(pendingTxns),
- };
- }
- }
- } else {
+ );
+ for (let i = 0; i < signedTxns.length; i++) {
+ const signedTxnPromise = sendSignedTransaction({
+ connection,
+ signedTransaction: signedTxns[i],
+ });
+
+ if (sequenceType !== SequenceType.Parallel) {
+ try {
+ await signedTxnPromise.then(({ txid, slot }) =>
+ successCallback(txid, i),
+ );
pendingTxns.push(signedTxnPromise);
+ } catch (e) {
+ console.log('Failed at txn index:', i);
+ console.log('Caught failure:', e);
+
+ failCallback(signedTxns[i], i);
+ if (sequenceType === SequenceType.StopOnFailure) {
+ return {
+ number: i,
+ txs: await Promise.all(pendingTxns),
+ };
+ }
}
+ } else {
+ pendingTxns.push(signedTxnPromise);
}
-
- if (sequenceType !== SequenceType.Parallel) {
- await Promise.all(pendingTxns);
- }
-
- return { number: signedTxns.length, txs: await Promise.all(pendingTxns) };
- };
-
- export const sendTransaction = async (
+ }
+
+ if (sequenceType !== SequenceType.Parallel) {
+ const result = await Promise.all(pendingTxns);
+ return { number: signedTxns.length, txs: result };
+ }
+
+ return { number: signedTxns.length, txs: await Promise.all(pendingTxns) };
+};
+
+export const sendTransaction = async (
connection: Connection,
wallet: any,
- instructions: TransactionInstruction[],
+ instructions: TransactionInstruction[] | Transaction,
signers: Keypair[],
awaitConfirmation = true,
commitment: Commitment = 'singleGossip',
includesFeePayer: boolean = false,
block?: BlockhashAndFeeCalculator,
- ) => {
- if (!wallet.publicKey) throw new WalletNotConnectedError();
-
- let transaction = new Transaction();
+) => {
+ if (!wallet.publicKey) throw new WalletNotConnectedError();
+
+ let transaction: Transaction;
+ if (!Array.isArray(instructions)) {
+ transaction = instructions;
+ } else {
+ transaction = new Transaction();
instructions.forEach(instruction => transaction.add(instruction));
transaction.recentBlockhash = (
- block || (await connection.getRecentBlockhash(commitment))
+ block || (await connection.getRecentBlockhash(commitment))
).blockhash;
-
+
if (includesFeePayer) {
transaction.setSigners(...signers.map(s => s.publicKey));
} else {
transaction.setSigners(
- // fee payed by the wallet owner
- wallet.publicKey,
- ...signers.map(s => s.publicKey),
+ // fee payed by the wallet owner
+ wallet.publicKey,
+ ...signers.map(s => s.publicKey),
);
}
-
+
if (signers.length > 0) {
transaction.partialSign(...signers);
}
if (!includesFeePayer) {
transaction = await wallet.signTransaction(transaction);
}
-
- const rawTransaction = transaction.serialize();
- let options = {
- skipPreflight: true,
- commitment,
- };
-
- const txid = await connection.sendRawTransaction(rawTransaction, options);
- let slot = 0;
-
- if (awaitConfirmation) {
- const confirmation = await awaitTransactionSignatureConfirmation(
+ }
+
+ const rawTransaction = transaction.serialize();
+ let options = {
+ skipPreflight: true,
+ commitment,
+ };
+
+ const txid = await connection.sendRawTransaction(rawTransaction, options);
+ let slot = 0;
+
+ if (awaitConfirmation) {
+ const confirmation = await awaitTransactionSignatureConfirmation(
txid,
DEFAULT_TIMEOUT,
connection,
commitment,
- );
-
- if (!confirmation)
- throw new Error('Timed out awaiting confirmation on transaction');
- slot = confirmation?.slot || 0;
-
- if (confirmation?.err) {
- const errors = await getErrorForTransaction(connection, txid);
-
- console.log(errors);
- throw new Error(`Raw transaction ${txid} failed`);
- }
+ );
+
+ if (!confirmation)
+ throw new Error('Timed out awaiting confirmation on transaction');
+ slot = confirmation?.slot || 0;
+
+ if (confirmation?.err) {
+ const errors = await getErrorForTransaction(connection, txid);
+
+ console.log(errors);
+ throw new Error(`Raw transaction ${txid} failed`);
}
-
- return { txid, slot };
- };
-
- export const sendTransactionWithRetry = async (
+ }
+
+ return { txid, slot };
+};
+
+export const sendTransactionWithRetry = async (
connection: Connection,
wallet: any,
instructions: TransactionInstruction[],
@@ -297,185 +306,184 @@ import {
includesFeePayer: boolean = false,
block?: BlockhashAndFeeCalculator,
beforeSend?: () => void,
- ) => {
- if (!wallet.publicKey) throw new WalletNotConnectedError();
-
- let transaction = new Transaction();
- instructions.forEach(instruction => transaction.add(instruction));
- transaction.recentBlockhash = (
+) => {
+ if (!wallet.publicKey) throw new WalletNotConnectedError();
+
+ let transaction = new Transaction();
+ instructions.forEach(instruction => transaction.add(instruction));
+ transaction.recentBlockhash = (
block || (await connection.getRecentBlockhash(commitment))
- ).blockhash;
-
- if (includesFeePayer) {
- transaction.setSigners(...signers.map(s => s.publicKey));
- } else {
- transaction.setSigners(
+ ).blockhash;
+
+ if (includesFeePayer) {
+ transaction.setSigners(...signers.map(s => s.publicKey));
+ } else {
+ transaction.setSigners(
// fee payed by the wallet owner
wallet.publicKey,
...signers.map(s => s.publicKey),
- );
- }
-
- if (signers.length > 0) {
- transaction.partialSign(...signers);
- }
- if (!includesFeePayer) {
- transaction = await wallet.signTransaction(transaction);
- }
-
- if (beforeSend) {
- beforeSend();
- }
-
- const { txid, slot } = await sendSignedTransaction({
- connection,
- signedTransaction: transaction,
- });
-
- return { txid, slot };
- };
-
- export const getUnixTs = () => {
- return new Date().getTime() / 1000;
- };
-
- const DEFAULT_TIMEOUT = 100000;
-
- export async function sendSignedTransaction({
- signedTransaction,
+ );
+ }
+
+ if (signers.length > 0) {
+ transaction.partialSign(...signers);
+ }
+ if (!includesFeePayer) {
+ transaction = await wallet.signTransaction(transaction);
+ }
+
+ if (beforeSend) {
+ beforeSend();
+ }
+
+ const { txid, slot } = await sendSignedTransaction({
connection,
- timeout = DEFAULT_TIMEOUT,
- }: {
- signedTransaction: Transaction;
- connection: Connection;
- sendingMessage?: string;
- sentMessage?: string;
- successMessage?: string;
- timeout?: number;
- }): Promise<{ txid: string; slot: number }> {
- const rawTransaction = signedTransaction.serialize();
- const startTime = getUnixTs();
- let slot = 0;
- const txid: TransactionSignature = await connection.sendRawTransaction(
+ signedTransaction: transaction,
+ });
+
+ return { txid, slot };
+};
+
+export const getUnixTs = () => {
+ return new Date().getTime() / 1000;
+};
+
+export async function sendSignedTransaction({
+ signedTransaction,
+ connection,
+ timeout = DEFAULT_TIMEOUT,
+ }: {
+ signedTransaction: Transaction;
+ connection: Connection;
+ sendingMessage?: string;
+ sentMessage?: string;
+ successMessage?: string;
+ timeout?: number;
+}): Promise<{ txid: string; slot: number }> {
+ const rawTransaction = signedTransaction.serialize();
+
+ const startTime = getUnixTs();
+ let slot = 0;
+ const txid: TransactionSignature = await connection.sendRawTransaction(
rawTransaction,
{
skipPreflight: true,
},
- );
-
- console.log('Started awaiting confirmation for', txid);
-
- let done = false;
- (async () => {
- while (!done && getUnixTs() - startTime < timeout) {
- connection.sendRawTransaction(rawTransaction, {
- skipPreflight: true,
- });
- await sleep(500);
- }
- })();
- try {
- const confirmation = await awaitTransactionSignatureConfirmation(
+ );
+
+ console.log('Started awaiting confirmation for', txid);
+
+ let done = false;
+ (async () => {
+ while (!done && getUnixTs() - startTime < timeout) {
+ connection.sendRawTransaction(rawTransaction, {
+ skipPreflight: true,
+ });
+ await sleep(500);
+ }
+ })();
+ try {
+ const confirmation = await awaitTransactionSignatureConfirmation(
txid,
timeout,
connection,
'recent',
true,
- );
-
- if (!confirmation)
- throw new Error('Timed out awaiting confirmation on transaction');
-
- if (confirmation.err) {
- console.error(confirmation.err);
- throw new Error('Transaction failed: Custom instruction error');
- }
-
- slot = confirmation?.slot || 0;
- } catch (err: any) {
- console.error('Timeout Error caught', err);
- if (err.timeout) {
- throw new Error('Timed out awaiting confirmation on transaction');
- }
- let simulateResult: SimulatedTransactionResponse | null = null;
- try {
- simulateResult = (
+ );
+
+ if (!confirmation)
+ throw new Error('Timed out awaiting confirmation on transaction');
+
+ if (confirmation.err) {
+ console.error(confirmation.err);
+ throw new Error('Transaction failed: Custom instruction error');
+ }
+
+ slot = confirmation?.slot || 0;
+ } catch (err: any) {
+ console.error('Timeout Error caught', err);
+ if (err.timeout) {
+ throw new Error('Timed out awaiting confirmation on transaction');
+ }
+ let simulateResult: SimulatedTransactionResponse | null = null;
+ try {
+ simulateResult = (
await simulateTransaction(connection, signedTransaction, 'single')
- ).value;
- } catch (e) { }
- if (simulateResult && simulateResult.err) {
- if (simulateResult.logs) {
- for (let i = simulateResult.logs.length - 1; i >= 0; --i) {
- const line = simulateResult.logs[i];
- if (line.startsWith('Program log: ')) {
- throw new Error(
+ ).value;
+ } catch (e) {}
+ if (simulateResult && simulateResult.err) {
+ if (simulateResult.logs) {
+ for (let i = simulateResult.logs.length - 1; i >= 0; --i) {
+ const line = simulateResult.logs[i];
+ if (line.startsWith('Program log: ')) {
+ throw new Error(
'Transaction failed: ' + line.slice('Program log: '.length),
- );
- }
+ );
}
}
- throw new Error(JSON.stringify(simulateResult.err));
}
- // throw new Error('Transaction failed');
- } finally {
- done = true;
+ throw new Error(JSON.stringify(simulateResult.err));
}
-
- console.log('Latency', txid, getUnixTs() - startTime);
- return { txid, slot };
+ // throw new Error('Transaction failed');
+ } finally {
+ done = true;
}
-
- async function simulateTransaction(
+
+ console.log('Latency', txid, getUnixTs() - startTime);
+ return { txid, slot };
+}
+
+async function simulateTransaction(
connection: Connection,
transaction: Transaction,
commitment: Commitment,
- ): Promise> {
- // @ts-ignore
- transaction.recentBlockhash = await connection._recentBlockhash(
+): Promise> {
+ // @ts-ignore
+ transaction.recentBlockhash = await connection._recentBlockhash(
// @ts-ignore
connection._disableBlockhashCaching,
- );
-
- const signData = transaction.serializeMessage();
- // @ts-ignore
- const wireTransaction = transaction._serialize(signData);
- const encodedTransaction = wireTransaction.toString('base64');
- const config: any = { encoding: 'base64', commitment };
- const args = [encodedTransaction, config];
-
- // @ts-ignore
- const res = await connection._rpcRequest('simulateTransaction', args);
- if (res.error) {
- throw new Error('failed to simulate transaction: ' + res.error.message);
- }
- return res.result;
+ );
+
+ const signData = transaction.serializeMessage();
+ // @ts-ignore
+ const wireTransaction = transaction._serialize(signData);
+ const encodedTransaction = wireTransaction.toString('base64');
+ const config: any = { encoding: 'base64', commitment };
+ const args = [encodedTransaction, config];
+
+ // @ts-ignore
+ const res = await connection._rpcRequest('simulateTransaction', args);
+ if (res.error) {
+ throw new Error('failed to simulate transaction: ' + res.error.message);
}
-
- async function awaitTransactionSignatureConfirmation(
+ return res.result;
+}
+
+async function awaitTransactionSignatureConfirmation(
txid: TransactionSignature,
timeout: number,
connection: Connection,
commitment: Commitment = 'recent',
queryStatus = false,
- ): Promise {
- let done = false;
- let status: SignatureStatus | null | void = {
- slot: 0,
- confirmations: 0,
- err: null,
- };
- let subId = 0;
- status = await new Promise(async (resolve, reject) => {
- setTimeout(() => {
- if (done) {
- return;
- }
- done = true;
- console.log('Rejecting for timeout...');
- reject({ timeout: true });
- }, timeout);
- try {
- subId = connection.onSignature(
+): Promise {
+ let done = false;
+ let status: SignatureStatus | null | void = {
+ slot: 0,
+ confirmations: 0,
+ err: null,
+ };
+ let subId = 0;
+ status = await new Promise(async (resolve, reject) => {
+ setTimeout(() => {
+ if (done) {
+ return;
+ }
+ done = true;
+ console.log('Rejecting for timeout...');
+ reject({ timeout: true });
+ }, timeout);
+ try {
+ subId = connection.onSignature(
txid,
(result, context) => {
done = true;
@@ -493,52 +501,49 @@ import {
}
},
commitment,
- );
- } catch (e) {
- done = true;
- console.error('WS error in setup', txid, e);
- }
- while (!done && queryStatus) {
- // eslint-disable-next-line no-loop-func
- (async () => {
- try {
- const signatureStatuses = await connection.getSignatureStatuses([
- txid,
- ]);
- status = signatureStatuses && signatureStatuses.value[0];
- if (!done) {
- if (!status) {
- console.log('REST null result for', txid, status);
- } else if (status.err) {
- console.log('REST error for', txid, status);
- done = true;
- reject(status.err);
- } else if (!status.confirmations) {
- console.log('REST no confirmations for', txid, status);
- } else {
- console.log('REST confirmation for', txid, status);
- done = true;
- resolve(status);
- }
- }
- } catch (e) {
- if (!done) {
- console.log('REST connection error: txid', txid, e);
+ );
+ } catch (e) {
+ done = true;
+ console.error('WS error in setup', txid, e);
+ }
+ while (!done && queryStatus) {
+ // eslint-disable-next-line no-loop-func
+ (async () => {
+ try {
+ const signatureStatuses = await connection.getSignatureStatuses([
+ txid,
+ ]);
+ status = signatureStatuses && signatureStatuses.value[0];
+ if (!done) {
+ if (!status) {
+ console.log('REST null result for', txid, status);
+ } else if (status.err) {
+ console.log('REST error for', txid, status);
+ done = true;
+ reject(status.err);
+ } else if (!status.confirmations) {
+ console.log('REST no confirmations for', txid, status);
+ } else {
+ console.log('REST confirmation for', txid, status);
+ done = true;
+ resolve(status);
}
}
- })();
- await sleep(2000);
- }
- });
-
- //@ts-ignore
- if (connection._signatureSubscriptions[subId])
- connection.removeSignatureListener(subId);
- done = true;
- console.log('Returning status', status);
- return status;
- }
- export function sleep(ms: number): Promise {
- return new Promise(resolve => setTimeout(resolve, ms));
- }
-
\ No newline at end of file
+ } catch (e) {
+ if (!done) {
+ console.log('REST connection error: txid', txid, e);
+ }
+ }
+ })();
+ await sleep(2000);
+ }
+ });
+
+
+ done = true;
+ console.log('Returning status', status);
+ return status;
+}
+export function sleep(ms: number): Promise {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
diff --git a/src/utils.ts b/src/utils.ts
index 59eeff7..5c438bf 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -11,6 +11,7 @@ export interface AlertState {
open: boolean;
message: string;
severity: 'success' | 'info' | 'warning' | 'error' | undefined;
+ hideDuration?: number | null;
}
export const toDate = (value?: anchor.BN) => {
@@ -45,51 +46,51 @@ export const formatNumber = {
};
export const SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID =
- new anchor.web3.PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL');
+ new anchor.web3.PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL');
export const CIVIC = new anchor.web3.PublicKey(
- 'gatem74V238djXdzWnJf94Wo1DcnuGkfijbf3AuBhfs',
+ 'gatem74V238djXdzWnJf94Wo1DcnuGkfijbf3AuBhfs',
);
export const getAtaForMint = async (
- mint: anchor.web3.PublicKey,
- buyer: anchor.web3.PublicKey,
+ mint: anchor.web3.PublicKey,
+ buyer: anchor.web3.PublicKey,
): Promise<[anchor.web3.PublicKey, number]> => {
return await anchor.web3.PublicKey.findProgramAddress(
- [buyer.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()],
- SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
+ [buyer.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()],
+ SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
);
};
export const getNetworkExpire = async (
- gatekeeperNetwork: anchor.web3.PublicKey,
+ gatekeeperNetwork: anchor.web3.PublicKey,
): Promise<[anchor.web3.PublicKey, number]> => {
return await anchor.web3.PublicKey.findProgramAddress(
- [gatekeeperNetwork.toBuffer(), Buffer.from('expire')],
- CIVIC,
+ [gatekeeperNetwork.toBuffer(), Buffer.from('expire')],
+ CIVIC,
);
};
export const getNetworkToken = async (
- wallet: anchor.web3.PublicKey,
- gatekeeperNetwork: anchor.web3.PublicKey,
+ wallet: anchor.web3.PublicKey,
+ gatekeeperNetwork: anchor.web3.PublicKey,
): Promise<[anchor.web3.PublicKey, number]> => {
return await anchor.web3.PublicKey.findProgramAddress(
- [
- wallet.toBuffer(),
- Buffer.from('gateway'),
- Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]),
- gatekeeperNetwork.toBuffer(),
- ],
- CIVIC,
+ [
+ wallet.toBuffer(),
+ Buffer.from('gateway'),
+ Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]),
+ gatekeeperNetwork.toBuffer(),
+ ],
+ CIVIC,
);
};
export function createAssociatedTokenAccountInstruction(
- associatedTokenAddress: anchor.web3.PublicKey,
- payer: anchor.web3.PublicKey,
- walletAddress: anchor.web3.PublicKey,
- splTokenMintAddress: anchor.web3.PublicKey,
+ associatedTokenAddress: anchor.web3.PublicKey,
+ payer: anchor.web3.PublicKey,
+ walletAddress: anchor.web3.PublicKey,
+ splTokenMintAddress: anchor.web3.PublicKey,
) {
const keys = [
{