How do I update an NFTs creator list? - solana

I'mn trying to update a list of nfts to have a new secondary creator (the one with the 100% share). I don't think it's possible to update the first creator because I think the first creator is signed by the candy machine that created the nft? Anyway here's my code:
import { keypairIdentity, Metadata, Metaplex } from '#metaplex-foundation/js'
import { Connection, Keypair, PublicKey } from '#solana/web3.js'
import { program } from 'commander'
import { readFileSync, writeFileSync } from 'fs'
program
.command('update_creators')
.description('Updates the creator of all nfts')
.requiredOption(
'-i, --input-file <string>',
'Json file, list of NFT mint details'
)
.requiredOption(
'-o, --output-file <string>',
'Output file for NFT addresses that failed to update'
)
.requiredOption(
'-k, --keypair-file <path>',
`JSON file containing private key of token owner`,
'.cache/creator-keypair.json'
)
.option('-r, --rpc <string>', 'JSON rpc api url', defaultRpc)
.action(async ({ inputFile, outputFile, keypairFile, rpc }) => {
const connection = new Connection(rpc)
const metaplex = Metaplex.make(connection)
const keypairFileContents = readFileSync(keypairFile, 'utf-8')
const keypair = Keypair.fromSecretKey(
Buffer.from(JSON.parse(keypairFileContents))
)
metaplex.use(keypairIdentity(keypair))
const nftMintAddresses = JSON.parse(
readFileSync(inputFile, 'utf-8')
) as string[]
let nfts = (await metaplex
.nfts()
.findAllByMintList({
mints: nftMintAddresses.map(mint => new PublicKey(mint)),
})
.run()) as Metadata[]
const newCreator = new PublicKey(
'CUAwUE5N3TdHHHyPTHb3E5mpnpQFiRF6BcY8kEvJakfS'
)
const failedNfts: any[] = []
for (const nft of nfts) {
try {
console.dir(nft, { depth: null })
const newNft = await metaplex
.nfts()
.update({
nftOrSft: nft,
creators: [
nft.creators[0],
{
address: newCreator,
share: 100,
authority: keypair,
},
],
})
.run()
console.dir(newNft, { depth: null })
} catch (e) {
console.error(e)
failedNfts.push(nft)
process.exit()
}
}
writeFileSync(outputFile, JSON.stringify(failedNfts))
})
Note, the metaplex.use() keypair I'm using is the same wallet used to create the candy machine and has authority to update the nfts, but I keep getting the following error:
ParsedProgramError [MetaplexError]: TokenMetadataProgram > Incorrect account owner
>> Source: Program > TokenMetadataProgram [metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s]
>> Problem: The program [TokenMetadataProgram] at address [metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s] raised an error of code [57] that translates to "Incorrect account owner".
>> Solution: Check the error message provided by the program.
Caused By: IncorrectOwner: Incorrect account owner
at RpcClient.parseProgramError (C:\xampp\htdocs\sol-tools\node_modules\#metaplex-foundation\js\src\plugins\rpcModule\RpcClient.ts:302:9)
at RpcClient.sendTransaction (C:\xampp\htdocs\sol-tools\node_modules\#metaplex-foundation\js\src\plugins\rpcModule\RpcClient.ts:87:18)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async RpcClient.sendAndConfirmTransaction (C:\xampp\htdocs\sol-tools\node_modules\#metaplex-foundation\js\src\plugins\rpcModule\RpcClient.ts:117:23)
at async TransactionBuilder.sendAndConfirm (C:\xampp\htdocs\sol-tools\node_modules\#metaplex-foundation\js\src\utils\TransactionBuilder.ts:189:22)
at async C:\xampp\htdocs\sol-tools\node_modules\#metaplex-foundation\js\src\utils\Task.ts:82:23
at async Disposable.run (C:\xampp\htdocs\sol-tools\node_modules\#metaplex-foundation\js\src\utils\Disposable.ts:34:14)
at async Command.<anonymous> (C:\xampp\htdocs\sol-tools\src\cli.ts:263:24) {
key: 'metaplex.errors.program.parsed_program_error',
title: 'TokenMetadataProgram > Incorrect account owner',
problem: 'The program [TokenMetadataProgram] at address [metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s] raised an error of code [57] that translates to "Incorrect account owner".',
solution: 'Check the error message provided by the program.',
source: 'program',
sourceDetails: 'TokenMetadataProgram [metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s]',
cause: IncorrectOwner: Incorrect account owner
at Object.errorResolver (C:\xampp\htdocs\sol-tools\node_modules\#metaplex-foundation\js\src\plugins\nftModule\plugin.ts:70:16)
at RpcClient.parseProgramError (C:\xampp\htdocs\sol-tools\node_modules\#metaplex-foundation\js\src\plugins\rpcModule\RpcClient.ts:299:35)
at RpcClient.sendTransaction (C:\xampp\htdocs\sol-tools\node_modules\#metaplex-foundation\js\src\plugins\rpcModule\RpcClient.ts:87:18)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async RpcClient.sendAndConfirmTransaction (C:\xampp\htdocs\sol-tools\node_modules\#metaplex-foundation\js\src\plugins\rpcModule\RpcClient.ts:117:23)
at async TransactionBuilder.sendAndConfirm (C:\xampp\htdocs\sol-tools\node_modules\#metaplex-foundation\js\src\utils\TransactionBuilder.ts:189:22)
at async C:\xampp\htdocs\sol-tools\node_modules\#metaplex-foundation\js\src\utils\Task.ts:82:23
at async Disposable.run (C:\xampp\htdocs\sol-tools\node_modules\#metaplex-foundation\js\src\utils\Disposable.ts:34:14)
at async Command.<anonymous> (C:\xampp\htdocs\sol-tools\src\cli.ts:263:24) {
code: 57
},
logs: undefined,
program: {
name: 'TokenMetadataProgram',
address: PublicKey {
_bn: <BN: b7065b1e3d17c45389d527f6b04c3cd58b86c731aa0fdb549b6d1bc03f82946>
},
errorResolver: [Function: errorResolver],
gpaResolver: [Function: gpaResolver]
}
}
And here's one of the NFTs I'm trying to update:
https://solscan.io/token/3woKb11Ajs9VkzHhMNkiyX5za1bV3STBmSaDHoQgmBKp#metadata
Any help would be appreciated. Thanks!

Related

Cypress task with xlsx leads to f.slice when using

I am getting this error when trying to access a cypress task masking the "xlsx": "^0.18.5"
for reading excel files.
From Node.js Internals:
TypeError: f.slice is not a function
at firstbyte (/local0/biologics/gitcheckouts/new-ui-e2e/node_modules/xlsx/xlsx.js:23626:38)
at readSync (/local0/biologics/gitcheckouts/new-ui-e2e/node_modules/xlsx/xlsx.js:23706:14)...
My task looks like this cypress.config.ts
on('task', {
readXlsx: readXlsx.read,
});
If I add brackets after the read:-> readXlsx: readXlsx.read() it already fails when starting cypress with the same error message:
Does anybody know what to do? (Cypress version 12.5.0)
Add on:
import * as fs from 'fs';
private validateExcelFile(countedItems: number, correction: number) {
cy.get('#LASTDOWNLOADEDFILE').then((name) => {
const fileName = name as unknown as string;
cy.log(`Validating excel file: ${fileName}`);
const buffer = fs.readFileSync(fileName);
cy.task('readXlsx', { buffer }).then((rows) => {
const rdw = (rows as string).length;
expect(rdw).to.be.equal(countedItems - correction);
});
});
}
I cannot tell what code, if any, precedes the task. The message seems to indicate a buffer issue, here is my working code you can compare to your own.
Otherwise, suspect the file itself.
import { readFileSync } from "fs";
import { read } from "xlsx/xlsx.mjs";
function read(name) {
const buffer = readFileSync(name);
const workbook = read(buffer);
return workbook
}
...
on('task', {
readXlsx: (name) => read(name)
...

How can I troubleshoot an error: lib/graphql has no exported mutation - for a mutation I have defined and which appears in graphql.tsx

I'm trying to figure out what I need to do in order to have lib/graphql recognise the mutations I have made.
I have an issue.tsx (which is a form). It imports:
import {
IssueInput,
useUpdateIssueMutation,
useAllIssuesQuery,
useCreateIssueMutation,
useDeleteIssueMutation,
Issue as IssueGQLType,
} from "lib/graphql"
Other than IssueInput and Issue, I'm getting errors in my terminal that say these queries and mutations are not exported members.
However when I try to load the issue page in local host, I get an error that says:
error - GraphQLError [Object]: Syntax Error: Expected Name, found
. It points to the line where Issue is imported.
I made all of these queries and mutations in my resolver as follows:
import { Arg, Mutation, Query, Resolver } from "type-graphql"
import { Issue } from "./issue.model"
import { IssueService } from "./issue.service"
import { IssueInput } from "./inputs/create.input"
import { Inject, Service } from "typedi"
import { UseAuth } from "../shared/middleware/UseAuth"
import { Role } from "#generated"
#Service()
#Resolver(() => Issue)
export default class IssueResolver {
#Inject(() => IssueService)
issueService: IssueService
#Query(() => [Issue])
async allIssues() {
return await this.issueService.getAllIssues()
}
#Query(() => [Issue])
async futureRiskIssues() {
return await this.issueService.getFutureRiskIssues()
}
#Query(() => Issue)
async issue(#Arg("id") id: string) {
return await this.issueService.getIssue(id)
}
#UseAuth([Role.ADMIN])
#Mutation(() => Issue)
async createIssue(#Arg("data") data: IssueInput) {
return await this.issueService.createIssue(data)
}
#UseAuth([Role.ADMIN])
#Mutation(() => Issue)
async deleteIssue(#Arg("id") id: string) {
return await this.issueService.deleteIssue(id)
}
#UseAuth([Role.ADMIN])
#Mutation(() => Issue)
async updateIssue(#Arg("id") id: string, #Arg("data") data: IssueInput) {
return await this.issueService.updateIssue(id, data)
}
}
I can also see from my graphql.tsx file, that these functions are recognised as follows:
export type Mutation = {
__typename?: 'Mutation';
createIssue: Issue;
createUser: User;
deleteIssue: Issue;
destroyAccount: Scalars['Boolean'];
forgotPassword: Scalars['Boolean'];
getBulkSignedS3UrlForPut?: Maybe<Array<SignedResponse>>;
getSignedS3UrlForPut?: Maybe<SignedResponse>;
login: AuthResponse;
register: AuthResponse;
resetPassword: Scalars['Boolean'];
updateIssue: Issue;
updateMe: User;
};
export type MutationCreateUserArgs = {
data: UserCreateInput;
};
export type MutationDeleteIssueArgs = {
id: Scalars['String'];
};
export type MutationUpdateIssueArgs = {
data: IssueInput;
id: Scalars['String'];
};
I have run the codegen several times and can't think of anything else to try to force these mutations and queries to be recognised. Can anyone see a way to trouble shoot this?
My codegen.yml has:
schema: http://localhost:5555/graphql
documents:
- "src/components/**/*.{ts,tsx}"
- "src/lib/**/*.{ts,tsx}"
- "src/pages/**/*.{ts,tsx}"
overwrite: true
generates:
src/lib/graphql.tsx:
config:
withMutationFn: false
addDocBlocks: false
scalars:
DateTime: string
plugins:
- add:
content: "/* eslint-disable */"
- typescript
- typescript-operations
- typescript-react-apollo
When I look at the mutations available on the authentication objects (that are provided with the [boilerplate app][1] that I am trying to use), I can see that there are mutations and queries that are differently represented in the lib/graphql file. I just can't figure out how to force the ones I write to be included in this way:
export function useLoginMutation(baseOptions?: Apollo.MutationHookOptions<LoginMutation, LoginMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useMutation<LoginMutation, LoginMutationVariables>(LoginDocument, options);
}
Instead, I get all of these things, but none of them look like the above and I can't figure out which one to import into my front end form so that I can make an entry in the database. None of them look like the queries or mutations I defined in my resolver
export type IssueInput = {
description: Scalars['String'];
issueGroup: Scalars['String'];
title: Scalars['String'];
};
export type IssueListRelationFilter = {
every?: InputMaybe<IssueWhereInput>;
none?: InputMaybe<IssueWhereInput>;
some?: InputMaybe<IssueWhereInput>;
};
export type IssueRelationFilter = {
is?: InputMaybe<IssueWhereInput>;
isNot?: InputMaybe<IssueWhereInput>;
};
export type IssueWhereInput = {
AND?: InputMaybe<Array<IssueWhereInput>>;
NOT?: InputMaybe<Array<IssueWhereInput>>;
OR?: InputMaybe<Array<IssueWhereInput>>;
createdAt?: InputMaybe<DateTimeFilter>;
description?: InputMaybe<StringFilter>;
id?: InputMaybe<UuidFilter>;
issueGroup?: InputMaybe<IssueGroupRelationFilter>;
issueGroupId?: InputMaybe<UuidFilter>;
subscribers?: InputMaybe<UserIssueListRelationFilter>;
title?: InputMaybe<StringFilter>;
updatedAt?: InputMaybe<DateTimeFilter>;
};
export type IssueWhereUniqueInput = {
id?: InputMaybe<Scalars['String']>;
};
I do have this record in my graphql.tsx file:
export type Mutation = {
__typename?: 'Mutation';
createIssue: Issue;
createIssueGroup: IssueGroup;
createUser: User;
deleteIssue: Issue;
deleteIssueGroup: IssueGroup;
destroyAccount: Scalars['Boolean'];
forgotPassword: Scalars['Boolean'];
getBulkSignedS3UrlForPut?: Maybe<Array<SignedResponse>>;
getSignedS3UrlForPut?: Maybe<SignedResponse>;
login: AuthResponse;
register: AuthResponse;
resetPassword: Scalars['Boolean'];
updateIssue: Issue;
updateIssueGroup: IssueGroup;
updateMe: User;
};
but I can't say: createIssueMutation as an import in my issue.tsx where I'm trying to make a form to use to post to the database.
[1]: https://github.com/NoQuarterTeam/boilerplate
In the issue form, I get an error that says:
"resource": "/.../src/pages/issue.tsx", "owner": "typescript",
"code": "2305", "severity": 8, "message": "Module '"lib/graphql"'
has no exported member 'useCreateIssueMutation'.", "source": "ts",
"startLineNumber": 7, "startColumn": 27, "endLineNumber": 7,
"endColumn": 54 }]
and the same thing for the query
check your codegen.yml
overwrite: true
schema: "http://localhost:4000/graphql"
documents: "src/graphql/**/*.graphql"
generates:
src/generated/graphql.tsx:
plugins:
- "typescript"
- "typescript-operations"
- "typescript-react-apollo"
./graphql.schema.json:
plugins:
- "introspection"
or try something like #Resolver(Issue)
It seems like you are not generating the hooks that you are trying to import.
You can update your codegen.yml file to add the generated hooks:
schema: http://localhost:5555/graphql
documents:
- "src/components/**/*.{ts,tsx}"
- "src/lib/**/*.{ts,tsx}"
- "src/pages/**/*.{ts,tsx}"
overwrite: true
generates:
src/lib/graphql.tsx:
config:
withMutationFn: false
addDocBlocks: false
scalars:
DateTime: string
withHooks: true # <--------------------- this line
plugins:
- add:
content: "/* eslint-disable */"
- typescript
- typescript-operations
- typescript-react-apollo

Deploying and Initializing anchor-escrow contract on devnet

this is more of a question than an actual issue
I want to use the contract
https://github.com/ironaddicteddog/anchor-escrow
to exchange tokens.
My plan is as follows, however I'm unsuccessful initializing the contract.
logs: [
'Program 11111111111111111111111111111111 invoke [1]',
'Create Account: account Address { address: 2yTRYBq58ZMgudQcEp18UnsCBPTUx9a12ZnzZ7N7v9hQ, base: None } already in use',
'Program 11111111111111111111111111111111 failed: custom program error: 0x0'
]
Note: 2yTRYBq58ZMgudQcEp18UnsCBPTUx9a12ZnzZ7N7v9hQ is the contract address in the lib.rs
Deploy program ✅ 2yTRYBq58ZMgudQcEp18UnsCBPTUx9a12ZnzZ7N7v9hQ
Create Token A DhS6x9pTrfCeY8iwkRdGstxUuphbeHqddT2vZSWRw3d2 and Token B HpdLmjjxD8YZav2S1aastqyLsKDUb1ToLDfq4hPsLBoc ✅
Create token accounts Token A account Cj6cMp4xfAaCFegMg9G7GQaZWqYbQqgmu7Vjd4BbGYHh, Token B account HpdLmjjxD8YZav2S1aastqyLsKDUb1ToLDfq4hPsLBoc✅
Initialize contract ❌.
Exchange tokens
I've written following script to initialize the contract.
import * as anchor from '#project-serum/anchor';
import { PublicKey, SystemProgram, Transaction, Connection, Commitment } from '#solana/web3.js';
import { TOKEN_PROGRAM_ID, Token } from "#solana/spl-token";
import {
escrowAccount,
initializerMainAccount,
initializerTokenAccountA,
initializerTokenAccountB,
mintAPublicKey,
mintBPublicKey } from './accounts'
import NodeWallet from '#project-serum/anchor/dist/cjs/nodewallet';
const takerAmount = 1000;
const initializerAmount = 500;
const commitment: Commitment = 'processed';
const connection = new Connection('https://api.devnet.solana.com', { commitment, wsEndpoint: 'wss://api.devnet.solana.com/' });
// const connection = new Connection('http://127.0.0.1:8899', { commitment, wsEndpoint: 'wss://127.0.0.1:8899/' });
const options = anchor.Provider.defaultOptions();
const wallet = new NodeWallet(initializerMainAccount);
const provider = new anchor.Provider(connection, wallet, options);
anchor.setProvider(provider);
// Read the generated IDL.
const idl = JSON.parse(
require("fs").readFileSync("./tests/keypair/anchor_escrow.json", "utf8")
);
// Address of the deployed program.
const programId = new anchor.web3.PublicKey("2yTRYBq58ZMgudQcEp18UnsCBPTUx9a12ZnzZ7N7v9hQ");
// Generate the program client from IDL.
const program = new anchor.Program(idl, programId);
const initEscrow = async () => {
const [_vault_account_pda, _vault_account_bump] = await PublicKey.findProgramAddress(
[Buffer.from(anchor.utils.bytes.utf8.encode("token-seed"))],
program.programId,
);
const vault_account_pda = _vault_account_pda;
const vault_account_bump = _vault_account_bump;
const [_vault_authority_pda, _vault_authority_bump] = await PublicKey.findProgramAddress(
[Buffer.from(anchor.utils.bytes.utf8.encode("escrow"))],
program.programId,
);
// DEBUG BEGIN
// console.info(`initializerMainAccount: ` + JSON.stringify(initializerMainAccount, null, 2));
// console.info(`Escrow account: ` + JSON.stringify(escrowAccount));
console.info(`Mint A: ` + mintAPublicKey.toBase58());
console.info(`Mint B: ` + mintBPublicKey.toBase58());
console.info(`TOKEN_PROGRAM_ID: ` + TOKEN_PROGRAM_ID);
console.info(`SYSVAR_RENT_PUBKEY: ` + anchor.web3.SYSVAR_RENT_PUBKEY);
// DEBUG CONSOLE END
await program.rpc.initialize(
vault_account_bump,
new anchor.BN(initializerAmount),
new anchor.BN(takerAmount),
{
accounts: {
initializer: initializerMainAccount.publicKey,
mint: mintAPublicKey,
vaultAccount: vault_account_pda,
initializerDepositTokenAccount: initializerTokenAccountA,
initializerReceiveTokenAccount: initializerTokenAccountB,
escrowAccount: escrowAccount.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
tokenProgram: TOKEN_PROGRAM_ID,
},
instructions: [
await program.account.escrowAccount.createInstruction(escrowAccount),
],
signers: [escrowAccount, initializerMainAccount],
}
);
}
initEscrow();
Long version of the error output is
➜ anchor-escrow git:(master) ✗ ts-node tests/init2.ts
Mint A: DhS6x9pTrfCeY8iwkRdGstxUuphbeHqddT2vZSWRw3d2
Mint B: HpdLmjjxD8YZav2S1aastqyLsKDUb1ToLDfq4hPsLBoc
TOKEN_PROGRAM_ID: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
SYSVAR_RENT_PUBKEY: SysvarRent111111111111111111111111111111111
Transaction simulation failed: Error processing Instruction 0: custom program error: 0x0
Program 11111111111111111111111111111111 invoke [1]
Create Account: account Address { address: 2yTRYBq58ZMgudQcEp18UnsCBPTUx9a12ZnzZ7N7v9hQ, base: None } already in use
Program 11111111111111111111111111111111 failed: custom program error: 0x0
/Users/tuncatunc/git/anchor-escrow/node_modules/#solana/web3.js/src/connection.ts:3961
throw new SendTransactionError(
^
SendTransactionError: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: custom program error: 0x0
at Connection.sendEncodedTransaction (/Users/tuncatunc/git/anchor-escrow/node_modules/#solana/web3.js/src/connection.ts:3961:13)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async Connection.sendRawTransaction (/Users/tuncatunc/git/anchor-escrow/node_modules/#solana/web3.js/src/connection.ts:3918:20)
at async sendAndConfirmRawTransaction (/Users/tuncatunc/git/anchor-escrow/node_modules/#solana/web3.js/src/util/send-and-confirm-raw-transaction.ts:27:21)
at async Provider.send (/Users/tuncatunc/git/anchor-escrow/node_modules/#project-serum/anchor/src/provider.ts:118:18)
at async Object.rpc [as initialize] (/Users/tuncatunc/git/anchor-escrow/node_modules/#project-serum/anchor/src/program/namespace/rpc.ts:25:23) {
logs: [
'Program 11111111111111111111111111111111 invoke [1]',
'Create Account: account Address { address: 2yTRYBq58ZMgudQcEp18UnsCBPTUx9a12ZnzZ7N7v9hQ, base: None } already in use',
'Program 11111111111111111111111111111111 failed: custom program error: 0x0'
]
}
This is a long post
Thx a lot in advance for writing this contract to guide solana contract devs.
BR
I'll answer my own question here,
I figured out that the escrow program is already initialized.
The error log says that, it cannot use the same escrow account to re-initialize.

Get Balance and all custom token list of Solana tokens in Wallet

I'm actually stuck at the first step on connecting to phantom wallet. I'm trying to perform the following step.
Connect to Phantom wallet
Get a Public key
Get Balance of all tokens
Perform Buy/Sell
I'm able to connect to a phantom wallet with the below code. I'm not sure if the next step in the process is to get a public key so that I can find the balance of all tokens as part of the account.
const balance = connection.getBalance(publicKey);
The above method is what I found from the documentation, but I'm not sure how can I get the publicKey of the end user who connected their wallet to the webiste.
const connection = new solanaWeb3.Connection(solanaWeb3.clusterApiUrl("mainnet-beta")) console.log(connection);
assume you have react app for integrating with solana wallets, first of all install these packages:
yarn add #solana/wallet-adapter-base \
#solana/wallet-adapter-react \
#solana/wallet-adapter-react-ui \
#solana/wallet-adapter-wallets \
#solana/web3.js \
react
you can use next, vue, angular, svelte and material ui as well
next we have this setup:
import React, { FC, useMemo } from 'react';
import { ConnectionProvider, WalletProvider } from '#solana/wallet-adapter-react';
import { WalletAdapterNetwork } from '#solana/wallet-adapter-base';
import {
//LedgerWalletAdapter,
PhantomWalletAdapter,
SolflareWalletAdapter,
//SlopeWalletAdapter,
//SolletExtensionWalletAdapter,
//SolletWalletAdapter,
//TorusWalletAdapter,
} from '#solana/wallet-adapter-wallets';
import {
WalletModalProvider,
WalletDisconnectButton,
WalletMultiButton
} from '#solana/wallet-adapter-react-ui';
import { clusterApiUrl } from '#solana/web3.js';
// Default styles that can be overridden by your app
require('#solana/wallet-adapter-react-ui/styles.css');
export const Wallet: FC = () => {
// The network can be set to 'devnet', 'testnet', or 'mainnet-beta'.
const network = WalletAdapterNetwork.Devnet;
// You can also provide a custom RPC endpoint.
const endpoint = useMemo(() => clusterApiUrl(network), [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
// of wallets that your users connect to will be loaded.
const wallets = useMemo(
() => [
new PhantomWalletAdapter(),
new SlopeWalletAdapter(),
new SolflareWalletAdapter(),
new TorusWalletAdapter(),
new LedgerWalletAdapter(),
new SolletWalletAdapter({ network }),
new SolletExtensionWalletAdapter({ network }),
],
[network]
);
return (
<ConnectionProvider endpoint={endpoint}>
<WalletProvider wallets={wallets} autoConnect>
<WalletModalProvider>
<WalletMultiButton />
<WalletDisconnectButton />
{ /* Your app's components go here, nested within the context providers. */ }
</WalletModalProvider>
</WalletProvider>
</ConnectionProvider>
);
};
after import modules, I commented some wallet adapters except phantom and solfare
also this code block is so important:
<ConnectionProvider endpoint={endpoint}>
<WalletProvider wallets={wallets} autoConnect>
<WalletModalProvider>
<WalletMultiButton />
<WalletDisconnectButton />
{ /* Your app's components go here, nested within the context
providers. */ }
</WalletModalProvider>
</WalletProvider>
</ConnectionProvider>
modals with functional buttons surrounded by
ConnectionProvider: prepare endpoint for wallets to connecting to and query wallet tokens
WalletProvider: prepare which wallets should be load in modals and ready to connect
and finally usage:
import { WalletNotConnectedError } from '#solana/wallet-adapter-base';
import { useConnection, useWallet } from '#solana/wallet-adapter-react';
import { Keypair, SystemProgram, Transaction } from '#solana/web3.js';
import React, { FC, useCallback } from 'react';
export const SendOneLamportToRandomAddress: FC = () => {
const { connection } = useConnection();
const { publicKey, sendTransaction } = useWallet();
const onClick = useCallback(async () => {
if (!publicKey) throw new WalletNotConnectedError();
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: publicKey,
toPubkey: Keypair.generate().publicKey,
lamports: 1,
})
);
const signature = await sendTransaction(transaction, connection);
await connection.confirmTransaction(signature, 'processed');
}, [publicKey, sendTransaction, connection]);
return (
<button onClick={onClick} disabled={!publicKey}>
Send 1 lamport to a random address!
</button>
);
};
so as you can see above this part
const { connection } = useConnection();
const { publicKey, sendTransaction } = useWallet();
was prepared for making a connection and give you connected wallet public key and also make use of sendTransaction function of connected wallet, sounds good!
other parts of code are obvous i think.
so, how many functions we have with wallet adapters like phantom? and what functions?
we can get public key, connecting(boolean), connected(boolean), readyState.
also we have some other main functions like:
connect
disconnect
sendTransaction
signTransaction
signAllTransactions
signMessage
you can see all of them in this github repo link
another point is if you use Anchor framework you should know that Anchor uses its own "Wallet" object to interact with the connected wallet and sign transactions on its behalf.
so In order to get an object compatible with Anchor's definition of a wallet, we can use yet another composable called useAnchorWallet. This will return a wallet object that can sign transactions.
const wallet = useAnchorWallet()
have fun

Maximum call stack size exceeded on GraphQL Upload with NestJS

I'm facing an error on file upload with GraphQL Upload using the ReadStream function:
error: 17:10:32.466+02:00 [ExceptionsHandler] Maximum call stack size exceeded
error: 17:10:32.467+02:00 [graphql] Maximum call stack size exceeded RangeError: Maximum call stack size exceeded
at ReadStream.open (/Users/xxxx/Documents/Xxxx/xxxxx/xxxxx-api/node_modules/fs-capacitor/lib/index.js:80:7)
at _openReadFs (internal/fs/streams.js:117:12)
at ReadStream.<anonymous> (internal/fs/streams.js:110:3)
at ReadStream.deprecated [as open] (internal/util.js:96:15)
at ReadStream.open (/Users/xxxx/Documents/Xxxxx/xxxx/xxxxx-api/node_modules/fs-capacitor/lib/index.js:90:11)
at _openReadFs (internal/fs/streams.js:117:12)
at ReadStream.<anonymous> (internal/fs/streams.js:110:3)
at ReadStream.deprecated [as open] (internal/util.js:96:15)
at ReadStream.open (/Users/xxxx/Documents/Xxxxx/xxxxx/xxxxx-api/node_modules/fs-capacitor/lib/index.js:90:11)
at _openReadFs (internal/fs/streams.js:117:12) {"stack":"RangeError: Maximum call stack size exceeded\n at ReadStream.open (/Users/xxxx/Documents/Xxxxxx/xxxxx/xxxx-api/node_modules/fs-capacitor/lib/index.js:80:7)\n at _openReadFs (internal/fs/streams.js:117:12)\n at ReadStream.<anonymous> (internal/fs/streams.js:110:3)\n at ReadStream.deprecated [as open] (internal/util.js:96:15)\n at ReadStream.open (/Users/xxxxx/Documents/Xxxxx/xxxxx/xxxxx-api/node_modules/fs-capacitor/lib/index.js:90:11)\n at _openReadFs (internal/fs/streams.js:117:12)\n at ReadStream.<anonymous> (internal/fs/streams.js:110:3)\n at ReadStream.deprecated [as open] (internal/util.js:96:15)\n at ReadStream.open (/Users/xxxx/Documents/Xxxxxx/xxxx/xxxxx-api/node_modules/fs-capacitor/lib/index.js:90:11)\n at _openReadFs (internal/fs/streams.js:117:12)"}
(node:44569) [DEP0135] DeprecationWarning: ReadStream.prototype.open() is deprecated
(Use `node --trace-deprecation ...` to show where the warning was created)
Here is the function I'm using to upload a file:
public async cleanUpload(upload: GraphqlUpload, oldName?: string) {
let uploadResponse: FileInfo;
try {
if (oldName) {
this.safeRemove(oldName);
}
uploadResponse = await this.uploadFile(
{
fileName: upload.filename,
stream: upload.createReadStream(),
mimetype: upload.mimetype,
},
{ isPublic: true, filter: imageFilterFunction },
);
return uploadResponse;
} catch (e) {
this.logger.error('unable to upload', e);
if (uploadResponse) {
this.safeRemove(uploadResponse.fileName);
}
throw e;
}
}
The solution was to downgrade the Node version to 12.18 from 14.17.
To keep using Node 14.17 what you can do is to disable Apollo's internal upload and use graphql-upload
Please see this comment which outline the approach quoted here.
For any future readers, here is how to fix the issue once and for all.
The problem is that #nestjs/graphql's dependency, apollo-server-core, depends on an old version of graphql-upload (v8.0) which has conflicts with newer versions of Node.js and various packages. Apollo Server v2.21.0 seems to have fixed this but #nestjs/graphql is still on v2.16.1. Furthermore, Apollo Server v3 will be removing the built-in graphql-upload.
The solution suggested in this comment is to disable Apollo Server's built-in handling of uploads and use your own. This can be done in 3 simple steps:
1. package.json
Remove the fs-capacitor and graphql-upload entries from the resolutions section if you added them, and install the latest version of graphql-upload (v11.0.0 at this time) package as a dependency.
2. src/app.module.ts
Disable Apollo Server's built-in upload handling and add the graphqlUploadExpress middleware to your application.
import { graphqlUploadExpress } from "graphql-upload"
import { MiddlewareConsumer, Module, NestModule } from "#nestjs/common"
#Module({
imports: [
GraphQLModule.forRoot({
uploads: false, // disable built-in upload handling
}),
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(graphqlUploadExpress()).forRoutes("graphql")
}
}
3. src/blog/post.resolver.ts (example resolver)
Remove the GraphQLUpload import from apollo-server-core and import from graphql-upload instead
// import { GraphQLUpload } from "apollo-server-core" <-- remove this
import { FileUpload, GraphQLUpload } from "graphql-upload"
#Mutation(() => Post)
async postCreate(
#Args("title") title: string,
#Args("body") body: string,
#Args("attachment", { type: () => GraphQLUpload }) attachment: Promise<FileUpload>,
) {
const { filename, mimetype, encoding, createReadStream } = await attachment
console.log("attachment:", filename, mimetype, encoding)
const stream = createReadStream()
stream.on("data", (chunk: Buffer) => /* do stuff with data here */)
}

Resources