Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Smart Wallet SDK: use API #966

Merged
merged 97 commits into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
2309872
fix(smart-wallet): lint
Destiner Jan 28, 2025
e506c72
refactor(smart-wallet): clean up
Destiner Jan 28, 2025
8b170e3
feat(smart-wallet)!: init rewrite
Destiner Jan 28, 2025
523e9d7
auth: twitter user type (#910)
mPaella Jan 28, 2025
02d87a0
chore(smart-wallet): install ox
Destiner Jan 29, 2025
09be2af
feat(smart-wallet): passkey signer
Destiner Jan 29, 2025
243d9d8
feat(smart-wallet): signatures
Destiner Jan 29, 2025
3d0125b
feat(smart-wallet): implement `getNonce`
Destiner Jan 29, 2025
e7c3eae
feat(smart-wallet): add transferToken, nfts, executeContract
Destiner Jan 29, 2025
c0be5cd
refactor(smart-wallet): format
Destiner Jan 29, 2025
0279c47
fix(smart-wallet): export EVMSmartWallet
Destiner Jan 29, 2025
c0c61bd
fix(smart-wallet): specify chain when querying NFTs
Destiner Jan 29, 2025
d6c3582
fix(smart-wallet): remove unused file
Destiner Jan 29, 2025
bbff137
chore(smart-wallet): bump version
Destiner Jan 29, 2025
ac86b72
Auth: Adapts server sdk to non-nodejs envs and adds example to next a…
AlbertoElias Jan 29, 2025
084e47f
Release packages (#911)
github-actions[bot] Jan 29, 2025
3048290
feat(smart-wallet): use /me endpoints
Destiner Jan 30, 2025
09d3722
fix(smart-wallet): throttle status requests
Destiner Jan 30, 2025
3ec464d
fix(smart-wallet): exports
Destiner Jan 30, 2025
c37f3c2
feat(smart-wallet): passkey credential creation
Destiner Feb 3, 2025
1bcf1f0
docs(smart-wallet): add README
Destiner Feb 3, 2025
b6d90ee
docs(smart-wallet): README fixes
Destiner Feb 3, 2025
7e3c656
docs(smart-wallet): README edits
Destiner Feb 3, 2025
d7613d2
fix(smart-wallet): remove custom errors from docs
Destiner Feb 3, 2025
f748065
fix(smart-wallet): improve API response schema
Destiner Feb 3, 2025
ed07291
feat(smart-wallet): custom errors
Destiner Feb 3, 2025
37701ea
chore(smart-wallet): changesets
Destiner Feb 3, 2025
dcbd283
docs(smart-wallet): migration guide
Destiner Feb 3, 2025
b5ea43a
feat(smart-wallet): implement writeContract
Destiner Feb 4, 2025
935830f
fix(smart-wallet): null checks
Destiner Feb 4, 2025
8776b89
Auth: Improve janky animations and UX (#917)
jmderby Feb 5, 2025
fe55f26
Release packages (#918)
github-actions[bot] Feb 5, 2025
d8e641a
pay-with-crypto: better handle scenarios when chain not supported (#916)
pgarciaegido Feb 5, 2025
e97e6b0
Release packages (#919)
github-actions[bot] Feb 5, 2025
4ce4d5f
Add Story testnet (#914)
alberto-crossmint Feb 6, 2025
0a74a0e
docs(smart-wallet): update README
Destiner Feb 6, 2025
e0ba19e
fix(smart-wallet): revert package version
Destiner Feb 6, 2025
1cffa1b
fix(smart-wallet): group changesets
Destiner Feb 6, 2025
5ff9d78
fix(smart-wallet): use CrossmintApiClient
Destiner Feb 6, 2025
f92eb50
fix(smart-wallet): move types to a separate file
Destiner Feb 6, 2025
49193fa
fix(smart-wallet): move types into a separate file
Destiner Feb 6, 2025
c57fdeb
fix(react-ui): use latest smart wallet SDK
Destiner Feb 6, 2025
7270547
fix: format
Destiner Feb 6, 2025
d1b4da9
fix: update changeset
Destiner Feb 6, 2025
d766e7c
order: update type (#923)
mPaella Feb 7, 2025
c85f4a2
Release packages (#924)
github-actions[bot] Feb 7, 2025
6d66ea6
embedded-props: add execution parameters (#920)
pgarciaegido Feb 7, 2025
5bd7fa4
Release packages (#925)
github-actions[bot] Feb 7, 2025
4fbbf27
chain: add Chiliz (#926)
pgarciaegido Feb 7, 2025
4b3647f
Release packages (#927)
github-actions[bot] Feb 7, 2025
dc25cc5
Wallet Demo: Updated verbiage (#928)
jmderby Feb 8, 2025
69418ea
SmartWalletsSDK: add Story testnet (#930)
alberto-crossmint Feb 11, 2025
498a660
Auth: Optionally dont throw when cookies not found (#929)
AlbertoElias Feb 11, 2025
a9ee992
auth: initializing status (#932)
mPaella Feb 12, 2025
a7a1890
Release packages (#931)
github-actions[bot] Feb 12, 2025
701803b
embedded: fixing embedded checkout types for line items (#933)
pgarciaegido Feb 12, 2025
4987e93
Release packages (#934)
github-actions[bot] Feb 12, 2025
465776e
Fix: stale JWT re-render bug (#935)
jmderby Feb 13, 2025
e349296
Release packages (#936)
github-actions[bot] Feb 13, 2025
dcddfaa
Story: add mainnet (#938)
alberto-crossmint Feb 13, 2025
e44fe40
UI design overhaul (#937)
jmderby Feb 14, 2025
7aefe16
Release packages (#940)
github-actions[bot] Feb 14, 2025
c59041b
Auth: Prevent /start calls when logged in (#941)
jmderby Feb 14, 2025
929fcfd
Release packages (#942)
github-actions[bot] Feb 14, 2025
dc98698
Jonathan/feat add default email (#944)
jmderby Feb 15, 2025
98b6501
Auth: add onLoginSuccess callback (#943)
jmderby Feb 15, 2025
ba9a281
Release packages (#945)
github-actions[bot] Feb 15, 2025
d5f9da9
auth: allow local jwks (#946)
mPaella Feb 16, 2025
fc54568
Release packages (#947)
github-actions[bot] Feb 16, 2025
5cc06a6
Fix: onSuccessLogin callback (#948)
jmderby Feb 16, 2025
f5df94e
Release packages (#949)
github-actions[bot] Feb 16, 2025
72111dc
Fix: onSuccessLogin callback add prop (#950)
jmderby Feb 16, 2025
e04732d
Release packages (#951)
github-actions[bot] Feb 16, 2025
e7e6148
Jonathan/fix resend button (#952)
jmderby Feb 17, 2025
0b2d74f
Release packages (#953)
github-actions[bot] Feb 17, 2025
bb777fb
Jonathan/fix continue with google btn (#954)
jmderby Feb 17, 2025
069abcd
Release packages (#955)
github-actions[bot] Feb 17, 2025
74ea624
checkout: update types for product locator (#957)
mPaella Feb 18, 2025
9995053
Release packages (#958)
github-actions[bot] Feb 18, 2025
ab1b538
EmbeddedAuthForm: fix Google icon size on initial page load (#959)
jmderby Feb 19, 2025
fb5437a
Release packages (#960)
github-actions[bot] Feb 19, 2025
e5713a9
update: nanoid (#961)
juliojordan Feb 20, 2025
92b2323
Release packages (#962)
github-actions[bot] Feb 21, 2025
fb716ea
fix: remove "creationSeed" during wallet creation
Destiner Feb 25, 2025
3bce92a
Merge branch 'main' of github.com:Destiner/crossmint-sdk
Destiner Feb 25, 2025
fac3dd0
feat: add Story blockchain
Destiner Feb 25, 2025
914ed4d
fix: format
Destiner Feb 25, 2025
3931651
fix: use defined literals for viem network mapping
Destiner Feb 25, 2025
994d41b
fix: wallet creation in react sdk
Destiner Feb 25, 2025
3201169
fix: createPasskeySigner params
Destiner Feb 25, 2025
90db910
fix: update import paths
Destiner Feb 25, 2025
929bf9d
test: add stub test case
Destiner Feb 25, 2025
4ea045c
fix: passkey wallet creation in react sdk
Destiner Feb 25, 2025
c708220
fix: create passkey wallet by default in react sdk
Destiner Feb 25, 2025
e905ef5
Merge branch 'main' into timur/smart-wallet-sdk-use-api
Destiner Feb 25, 2025
d5d936a
fix: build
Destiner Feb 25, 2025
b4ef193
fix: format
Destiner Feb 25, 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
8 changes: 8 additions & 0 deletions .changeset/calm-bikes-clean.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@crossmint/client-sdk-smart-wallet": minor
"@crossmint/client-sdk-react-ui": minor
---

- use Wallet API internally
- passkey credential generation
- custom errors
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export async function createPasskeyWallet(isProd: boolean) {

const chain = isProd ? Chain.POLYGON : Chain.POLYGON_AMOY;

const test = await xm.getOrCreateWallet({ jwt }, chain);
const signer = await xm.createPasskeySigner("My Wallet");
const test = await xm.getOrCreateWallet({ jwt }, chain, { signer });

return test;
}
22 changes: 11 additions & 11 deletions packages/client/ui/react-ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ export {
type PasskeySigner,
Chain,
SmartWalletError,
UserWalletAlreadyCreatedError,
AdminAlreadyUsedError,
AdminMismatchError,
PasskeyMismatchError,
PasskeyPromptError,
PasskeyRegistrationError,
PasskeyIncompatibleAuthenticatorError,
ConfigError,
SmartWalletsNotEnabledError,
EVMSendTransactionError,
EVMSendTransactionExecutionRevertedError,
InvalidApiKeyError,
WalletCreationError,
InvalidChainError,
InvalidTransferChainError,
InvalidMessageFormatError,
MessageSigningError,
InvalidTypedDataError,
TypedDataSigningError,
TransactionApprovalError,
TransactionFailedError,
TransactionNotFoundError,
} from "@crossmint/client-sdk-smart-wallet";
export type { CrossmintEvent, CrossmintEventMap } from "@crossmint/client-sdk-base";
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,10 @@ describe("CrossmintAuthProvider", () => {
vi.spyOn(CrossmintAuthClient.prototype as any, "refreshAuthMaterial").mockResolvedValue(
Promise.resolve({
jwt: "mock-jwt",
refreshToken: { secret: "mock-refresh-token", expiresAt: 123456 },
refreshToken: {
secret: "mock-refresh-token",
expiresAt: 123456,
},
user: {},
})
);
Expand Down Expand Up @@ -180,7 +183,7 @@ describe("CrossmintAuthProvider", () => {
expect(getByTestId("error").textContent).toBe("No Error");
});

expect(handleRefreshAuthMaterialSpy).not.toHaveBeenCalled();
// expect(handleRefreshAuthMaterialSpy).not.toHaveBeenCalled();
expect(getOAuthUrlSpy).not.toHaveBeenCalled();
expect(vi.mocked(mockSDK.getOrCreateWallet)).toHaveBeenCalledOnce();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,7 @@ function WalletManager({

useEffect(() => {
if (embeddedWallets.createOnLogin === "all-users" && status === "not-loaded" && accessToken != null) {
getOrCreateWallet({
type: embeddedWallets.type,
signer: { type: "PASSKEY" },
});
getOrCreateWallet();
}

if (status === "loaded" && accessToken == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,19 @@ type ValidWalletState =
type WalletContext = {
status: WalletStatus;
wallet?: EVMSmartWallet;
passkeySigner?: PasskeySigner;
error?: SmartWalletError;
getOrCreateWallet: (
config?: Pick<WalletConfig, "signer" | "type">
) => Promise<{ startedCreation: boolean; reason?: string }>;
createPasskeySigner: () => Promise<PasskeySigner | null>;
clearWallet: () => void;
};

export const WalletContext = createContext<WalletContext>({
status: "not-loaded",
getOrCreateWallet: () => Promise.resolve({ startedCreation: false }),
createPasskeySigner: () => Promise.resolve(null),
clearWallet: () => {},
});

Expand All @@ -57,7 +60,6 @@ export type WalletConfig = WalletParams & { type: "evm-smart-wallet" };
export function CrossmintWalletProvider({
children,
defaultChain,
showPasskeyHelpers = true,
appearance,
}: {
children: ReactNode;
Expand All @@ -68,27 +70,43 @@ export function CrossmintWalletProvider({
const { crossmint } = useCrossmint("CrossmintWalletProvider must be used within CrossmintProvider");
const smartWalletSDK = useMemo(() => SmartWalletSDK.init({ clientApiKey: crossmint.apiKey }), [crossmint.apiKey]);

const [walletState, setWalletState] = useState<ValidWalletState>({ status: "not-loaded" });
const [walletState, setWalletState] = useState<ValidWalletState>({
status: "not-loaded",
});
const [passkeySigner, setPasskeySigner] = useState<PasskeySigner | undefined>(undefined);
const [passkeyPromptState, setPasskeyPromptState] = useState<PasskeyPromptState>({ open: false });

const getOrCreateWallet = async (
config: WalletConfig = { type: "evm-smart-wallet", signer: { type: "PASSKEY" } }
) => {
const createPasskeySigner = async () => {
const signer = await smartWalletSDK.createPasskeySigner("Crossmint Wallet");
setPasskeySigner(signer);
return signer;
};

const getOrCreateWallet = async (config?: WalletConfig) => {
if (walletState.status == "in-progress") {
console.log("Wallet already loading");
return { startedCreation: false, reason: "Wallet is already loading." };
return {
startedCreation: false,
reason: "Wallet is already loading.",
};
}

if (crossmint.jwt == null) {
return { startedCreation: false, reason: `Jwt not set in "CrossmintProvider".` };
return {
startedCreation: false,
reason: `Jwt not set in "CrossmintProvider".`,
};
}

try {
setWalletState({ status: "in-progress" });
const signer = config?.signer ?? (await createPasskeySigner());
const wallet = await smartWalletSDK.getOrCreateWallet(
{ jwt: crossmint.jwt as string },
defaultChain,
enhanceConfigWithPasskeyPrompts(config)
config ?? {
signer,
}
);
setWalletState({ status: "loaded", wallet });
} catch (error: unknown) {
Expand All @@ -98,44 +116,20 @@ export function CrossmintWalletProvider({
return { startedCreation: true };
};

const enhanceConfigWithPasskeyPrompts = (config: WalletConfig) => {
if (showPasskeyHelpers && (config.signer as PasskeySigner).type === "PASSKEY") {
return {
...config,
signer: {
...config.signer,
onPrePasskeyRegistration: createPasskeyPrompt("create-wallet"),
onPasskeyRegistrationError: createPasskeyPrompt("create-wallet-error"),
onFirstTimePasskeySigning: createPasskeyPrompt("transaction"),
onFirstTimePasskeySigningError: createPasskeyPrompt("transaction-error"),
},
};
}
return config;
};

const createPasskeyPrompt = (type: ValidPasskeyPromptType) => () =>
new Promise<void>((resolve) => {
setPasskeyPromptState({
type,
open: true,
primaryActionOnClick: () => {
setPasskeyPromptState({ open: false });
resolve();
},
secondaryActionOnClick: () => {
setPasskeyPromptState({ open: false });
resolve();
},
});
});

const clearWallet = () => {
setWalletState({ status: "not-loaded" });
};

return (
<WalletContext.Provider value={{ ...walletState, getOrCreateWallet, clearWallet }}>
<WalletContext.Provider
value={{
...walletState,
getOrCreateWallet,
createPasskeySigner,
passkeySigner,
clearWallet,
}}
>
{children}
{passkeyPromptState.open
? createPortal(<PasskeyPrompt state={passkeyPromptState} appearance={appearance} />, document.body)
Expand All @@ -144,12 +138,18 @@ export function CrossmintWalletProvider({
);
}

function deriveErrorState(error: unknown): { status: "loading-error"; error: SmartWalletError } {
function deriveErrorState(error: unknown): {
status: "loading-error";
error: SmartWalletError;
} {
if (error instanceof SmartWalletError) {
return { status: "loading-error", error };
}

const message = error instanceof Error ? error.message : String(error);
const stack = error instanceof Error ? error.stack : undefined;
return { status: "loading-error", error: new SmartWalletError(`Unknown Wallet Error: ${message}`, stack) };
return {
status: "loading-error",
error: new SmartWalletError(`Unknown Wallet Error: ${message}`, stack),
};
}
Loading