Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9b84edc
Revert "feat: solana nft demo (#1578)"
May 2, 2025
1ec29b6
Revert "Revert "feat: solana nft demo (#1578)""
May 2, 2025
f588284
Merge branch 'main' into bluj/re-do-solana-demo
Blu-J May 2, 2025
70301ed
Revert "Revert "feat: solana nft demo (#1578)" (#1582)"
May 2, 2025
014f4ab
fix: pr changes
May 2, 2025
c056018
chore: fix test that has a timing issue maybe
May 2, 2025
8e0b5b8
Merge branch 'main' of github.com:alchemyplatform/aa-sdk into bluj/re…
May 2, 2025
2941707
fix: allow the one card to do all
May 2, 2025
2ced951
Update examples/ui-demo/src/components/small-cards/SolanaNftCard.tsx
Blu-J May 5, 2025
a8a7491
feat: do auto from airdrop
May 5, 2025
cb02dad
chore: refactor to invert logic to injectable (#1584)
Blu-J May 6, 2025
41b3c69
feat: remove the address from the solana card
May 6, 2025
20407c7
fix: link to transaction needs three lines for card
May 6, 2025
682fc43
chore: remove old new badges for other cards
May 6, 2025
935bc81
fix: imports are not .js
May 6, 2025
183a4c9
Merge branch 'main' of github.com:alchemyplatform/aa-sdk into bluj/re…
May 6, 2025
7dea87f
chore: undo stage change
May 6, 2025
3817605
fix: weird bug on import
May 6, 2025
482ea3f
chore: change the function
May 6, 2025
879b10a
Merge branch 'main' of github.com:alchemyplatform/aa-sdk into bluj/re…
May 6, 2025
5f0c670
chore: add in the doc build
May 6, 2025
37da72a
chore: change the sponsoring to include rent
May 6, 2025
2d60611
fix: styling in card to match comments made in pr
May 7, 2025
fa66812
Merge branch 'main' of github.com:alchemyplatform/aa-sdk into bluj/re…
May 7, 2025
16e3459
Merge branch 'main' into bluj/re-do-solana-demo
Blu-J May 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions account-kit/react/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export * from "./useSignTypedData.js";
export * from "./useSmartAccountClient.js";
export * from "./useSolanaSigner.js";
export * from "./useSolanaTransaction.js";
export * from "./useSolanaConnection.js";
export * from "./useUiConfig.js";
export * from "./useUser.js";
export * from "./useWaitForUserOperationTransaction.js";
55 changes: 55 additions & 0 deletions account-kit/react/src/hooks/useSolanaConnection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"use client";

import * as solanaNetwork from "../solanaNetwork.js";
import { SolanaSigner } from "@account-kit/signer";
import { useSolanaSigner } from "./useSolanaSigner.js";
import { getSolanaConnection, watchSolanaConnection } from "@account-kit/core";
import { useSyncExternalStore } from "react";
import { useAlchemyAccountContext } from "./useAlchemyAccountContext.js";

/**
* Returned from the solana connection.
*/
export interface SolanaConnection {
/** The solana signer used to send the transaction */
readonly signer: SolanaSigner | null;
/** The solana connection used to send the transaction */
readonly connection: solanaNetwork.Connection | null;
}

/**
* The parameters for the SolanaConnectionHookParams hook.
*/
export type SolanaConnectionHookParams = {
signer?: SolanaSigner;
};

/**
* This hook is used for establishing a connection to Solana and returns the connection object and the signer object.
*
* @example
* ```ts
const {connection} = useSolanaConnection();

* ```
* @param {SolanaConnectionHookParams} opts Options for the hook to get setup
* @returns {SolanaConnection} The transaction hook.
*/
export function useSolanaConnection(
opts: SolanaConnectionHookParams = {}
): SolanaConnection {
const { config } = useAlchemyAccountContext();
const fallbackSigner: null | SolanaSigner = useSolanaSigner();
const connection =
useSyncExternalStore(
watchSolanaConnection(config),
() => getSolanaConnection(config),
() => getSolanaConnection(config)
)?.connection || null;
const signer: null | SolanaSigner = opts?.signer || fallbackSigner;

return {
connection,
signer,
};
}
115 changes: 95 additions & 20 deletions account-kit/react/src/hooks/useSolanaTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,43 @@ import type { BaseHookMutationArgs } from "../types.js";
import {
PublicKey,
SystemProgram,
Transaction,
TransactionInstruction,
VersionedTransaction,
} from "@solana/web3.js";
import { useSolanaSigner } from "./useSolanaSigner.js";
import { getSolanaConnection, watchSolanaConnection } from "@account-kit/core";
import { useSyncExternalStore } from "react";
import { useAlchemyAccountContext } from "./useAlchemyAccountContext.js";
import type { PromiseOrValue } from "../../../../aa-sdk/core/dist/types/types.js";

/** Used right before we send the transaction out, this is going to be the signer. */
export type PreSend = (
this: void,
transaction: VersionedTransaction | Transaction
) => PromiseOrValue<VersionedTransaction | Transaction>;
/**
* Used in the sendTransaction, will transform either the instructions (or the transfer -> instructions) into a transaction
*/
export type TransformInstruction = (
this: void,
instructions: TransactionInstruction[]
) => PromiseOrValue<Transaction | VersionedTransaction>;
export type SolanaTransactionParamOptions = {
preSend?: PreSend;
transformInstruction?: TransformInstruction;
};
export type SolanaTransactionParams =
| {
transfer: {
amount: number;
toAddress: string;
};
transactionComponents?: SolanaTransactionParamOptions;
}
| {
instructions: TransactionInstruction[];
transactionComponents?: SolanaTransactionParamOptions;
};
/**
* We wanted to make sure that this will be using the same useMutation that the
Expand Down Expand Up @@ -97,34 +118,84 @@ export function useSolanaTransaction(
() => getSolanaConnection(config)
);
const mutation = useMutation({
mutationFn: async (params: SolanaTransactionParams) => {
if (!signer) throw new Error("Not ready");
if (!connection) throw new Error("Not ready");
const instructions =
"instructions" in params
? params.instructions
: [
SystemProgram.transfer({
fromPubkey: new PublicKey(signer.address),
toPubkey: new PublicKey(params.transfer.toAddress),
lamports: params.transfer.amount,
}),
];
const policyId =
"policyId" in opts ? opts.policyId : backupConnection?.policyId;
const transaction = policyId
? await signer.addSponsorship(instructions, connection, policyId)
: await signer.createTransfer(instructions, connection);
mutationFn: async ({
transactionComponents: {
preSend,
transformInstruction = mapTransformInstructions.default,
} = {},
...params
}: SolanaTransactionParams) => {
const instructions = getInstructions();
let transaction: VersionedTransaction | Transaction =
await transformInstruction(instructions);

await signer.addSignature(transaction);
transaction = (await preSend?.(transaction)) || transaction;

const hash = await solanaNetwork.broadcast(connection, transaction);
if (needsSignerToSign()) {
await signer?.addSignature(transaction);
}

const localConnection = connection || missing("connection");
const hash = await solanaNetwork.broadcast(localConnection, transaction);
return { hash };

function getInstructions() {
if ("instructions" in params) {
return params.instructions;
}
return [
SystemProgram.transfer({
fromPubkey: new PublicKey(
signer?.address || missing("signer.address")
),
toPubkey: new PublicKey(params.transfer.toAddress),
lamports: params.transfer.amount,
}),
];
}

function needsSignerToSign() {
if ("message" in transaction) {
const message = transaction.message;
return message.staticAccountKeys.some(
(key, index) =>
key.toBase58() === signer?.address &&
message?.isAccountSigner(index)
);
}
return transaction.instructions.some((x) =>
x.keys.some(
(x) => x.pubkey.toBase58() === signer?.address && x.isSigner
)
);
}
},
...opts.mutation,
});
const signer: null | SolanaSigner = opts?.signer || fallbackSigner;
const connection = opts?.connection || backupConnection?.connection || null;
const policyId =
"policyId" in opts ? opts.policyId : backupConnection?.policyId;
const mapTransformInstructions: Record<string, TransformInstruction> = {
async addSponsorship(instructions: TransactionInstruction[]) {
return await (signer || missing("signer")).addSponsorship(
instructions,
connection || missing("connection"),
policyId || missing("policyId")
);
},
async createTransaction(instructions: TransactionInstruction[]) {
return await (signer || missing("signer")).createTransaction(
instructions,
connection || missing("connection")
);
},
get default() {
return policyId
? mapTransformInstructions.addSponsorship
: mapTransformInstructions.createTransaction;
},
};

return {
connection,
Expand All @@ -134,3 +205,7 @@ export function useSolanaTransaction(
sendTransactionAsync: mutation.mutateAsync,
};
}

function missing(message: string): never {
throw new Error(message);
}
4 changes: 4 additions & 0 deletions account-kit/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,9 @@ export {
useSolanaTransaction,
type SolanaTransaction,
} from "./hooks/useSolanaTransaction.js";
export {
useSolanaConnection,
type SolanaConnection,
} from "./hooks/useSolanaConnection.js";

export { useSolanaSignMessage } from "./hooks/useSolanaSignMessage.js";
8 changes: 4 additions & 4 deletions account-kit/signer/src/solanaSigner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,17 @@ export class SolanaSigner {
return toBytes(this.formatSignatureForSolana(signature));
}

async createTransfer(
async createTransaction(
instructions: TransactionInstruction[],
connection: Connection,
version?: "versioned"
): Promise<VersionedTransaction>;
async createTransfer(
async createTransaction(
instructions: TransactionInstruction[],
connection: Connection,
version?: "legacy"
): Promise<Transaction>;
async createTransfer(
async createTransaction(
instructions: TransactionInstruction[],
connection: Connection
): Promise<VersionedTransaction>;
Expand All @@ -111,7 +111,7 @@ export class SolanaSigner {
* @param {"versioned" | "legacy"} [version] - The version of the transaction
* @returns {Promise<Transaction | VersionedTransaction>} The transfer transaction
*/
async createTransfer(
async createTransaction(
instructions: TransactionInstruction[],
connection: Connection,
version?: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import { LocalAccountSigner } from "@aa-sdk/core";
import { LocalAccountSigner } from "@aa-sdk/core";
import { generatePrivateKey } from "viem";

const signer = LocalAccountSigner.mnemonicToAccountSigner(generatePrivateKey());
const signer = LocalAccountSigner.privateKeyToAccountSigner(
generatePrivateKey()
);
const address = await signer.getAddress();
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import { LocalAccountSigner } from "@aa-sdk/core";
import { LocalAccountSigner } from "@aa-sdk/core";
import { generatePrivateKey } from "viem";

const signer = LocalAccountSigner.mnemonicToAccountSigner(generatePrivateKey());
const signer = LocalAccountSigner.privateKeyToAccountSigner(
generatePrivateKey()
);
const signature = await signer.signMessage("Hello, world!");
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import { LocalAccountSigner } from "@aa-sdk/core";
import { LocalAccountSigner } from "@aa-sdk/core";
import { generatePrivateKey } from "viem";

const signer = LocalAccountSigner.mnemonicToAccountSigner(generatePrivateKey());
const signer = LocalAccountSigner.privateKeyToAccountSigner(
generatePrivateKey()
);
const signature = await signer.signTypedData({
domain: {},
types: {},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
# This file is autogenerated
title: useSolanaConnection
description: Overview of the useSolanaConnection method
slug: wallets/reference/account-kit/react/hooks/useSolanaConnection
---

This hook is used for establishing a connection to Solana and returns the connection object and the signer object.

## Import

```ts
import { useSolanaConnection } from "@account-kit/react";
```

## Usage

```ts
const { connection } = useSolanaConnection();
```

## Parameters

### opts

`SolanaConnectionHookParams`
Options for the hook to get setup

## Returns

`SolanaConnection`
The transaction hook.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
# This file is autogenerated
title: createTransaction
description: Overview of the createTransaction method
slug: wallets/reference/account-kit/signer/classes/SolanaSigner/createTransaction
---

Creates a transfer transaction. Used for the SolanaCard example.

## Import

```ts
import { SolanaSigner } from "@account-kit/signer";
```

## Parameters

### instructions

`TransactionInstruction[]`

- The instructions to add to the transaction

### connection

`Connection`

- The connection to use for the transaction

### version

`"versioned" | "legacy"`

- The version of the transaction

## Returns

`Promise<Transaction | VersionedTransaction>`
The transfer transaction
5 changes: 5 additions & 0 deletions examples/ui-demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,16 @@
"@account-kit/infra": "^4.2.0",
"@account-kit/logging": "^4.2.0",
"@account-kit/react": "^4.2.0",
"@metaplex-foundation/umi": "^1.2.0",
"@metaplex-foundation/umi-bundle-defaults": "^1.2.0",
"@metaplex-foundation/umi-rpc-web3js": "^1.2.0",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-toast": "^1.2.1",
"@radix-ui/react-tooltip": "^1.1.2",
"@solana/spl-token": "^0.4.13",
"@solana/spl-token-metadata": "^0.1.6",
"@solana/web3.js": "^1.98.0",
"@t3-oss/env-core": "^0.7.1",
"@t3-oss/env-nextjs": "^0.7.1",
Expand Down
Binary file added examples/ui-demo/public/images/duckImage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion examples/ui-demo/src/app/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export const alchemyConfig = () => {
enablePopupOauth: true,
solana: {
connection: solanaConnection,
// policyId: process.env.NEXT_PUBLIC_PAYMASTER_POLICY_ID,
policyId: process.env.NEXT_PUBLIC_SOLANA_POLICY_ID,
},
},
{
Expand Down
Loading
Loading