Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a2de83c
crypto: add teddsa_core Rust library
Ryz0nd Dec 23, 2025
aa429d2
crypto: add teddsa_wasm WASM bindings
Ryz0nd Dec 23, 2025
156725a
crypto: add teddsa_hooks TypeScript wrappers
Ryz0nd Dec 23, 2025
0fea01b
crypto: add teddsa_hooks TypeScript wrappers
Ryz0nd Dec 23, 2025
8d7e774
teddsa: remove teddsa_core
Ryz0nd Dec 24, 2025
1040216
teddsa: use frost_ed25519_keplr directly
Ryz0nd Dec 24, 2025
a86026d
o
chemonoworld Dec 24, 2025
21b9cfa
teddsa: refactor and implement keygen and signing for Ed25519(combine)
chemonoworld Dec 24, 2025
185e1c6
teddsa: remove unused teddsa_hooks
Ryz0nd Dec 24, 2025
47666d3
Revert "teddsa: remove unused teddsa_hooks"
Ryz0nd Dec 24, 2025
292c19e
teddsa: implement Ed25519 key recovery
Ryz0nd Dec 27, 2025
9d7c222
o
chemonoworld Dec 29, 2025
87d2560
Revert "teddsa: implement Ed25519 key recovery"
Ryz0nd Dec 30, 2025
2749a7f
oko_attached: add Ed25519 keygen support
Ryz0nd Dec 23, 2025
aab61f9
oko_attached: add Ed25519 signing flow
Ryz0nd Dec 23, 2025
b3362c4
oko_attached: add Solana signature modal UI
Ryz0nd Dec 23, 2025
e5036ab
oko-sdk-core: add Ed25519 public key method
Ryz0nd Dec 23, 2025
a344b10
oko_sdk_sol: update for Ed25519 signing
Ryz0nd Dec 23, 2025
5e48a94
o
Ryz0nd Dec 24, 2025
954f270
teddsa: replace teddsa-wasm with frost-ed25519-keplr-wasm
Ryz0nd Dec 24, 2025
041e33d
sandbox_sol: refactor UI to Tailwind CSS
Ryz0nd Dec 28, 2025
920d76c
oko_attached: add Ed25519 recovery to reshare
Ryz0nd Dec 28, 2025
0859533
oko_sdk_sol: implement wallet-standard
Ryz0nd Jan 4, 2026
f249641
oko_sdk_sol: update signing methods
Ryz0nd Jan 4, 2026
96a8d02
oko_sdk_sol: add tests
Ryz0nd Jan 4, 2026
3defc43
oko_attached: refactor Solana signing modals
Ryz0nd Jan 4, 2026
fb98a92
sandbox_sol: add wallet-adapter test
Ryz0nd Jan 4, 2026
013fab2
docs: add Solana integration guide
Ryz0nd Jan 4, 2026
d45c4ea
o
Ryz0nd Jan 5, 2026
69e53b8
o
Ryz0nd Jan 5, 2026
6c94d5b
tss_api: add user_identifier support to Ed25519 keygen
Ryz0nd Jan 5, 2026
6a84f48
o
Ryz0nd Jan 5, 2026
17fa8c6
tss_api: add user_identifier support to Ed25519 keygen
Ryz0nd Jan 5, 2026
6716ea4
o
Ryz0nd Jan 5, 2026
79920c2
oko_attached: add solana s3 url
Ryz0nd Jan 5, 2026
0510c39
oko_sdk_sol: fix event listener leak on disconnect
Ryz0nd Jan 5, 2026
4e58db9
oko_sdk_sol: add validation for signAllTransactions
Ryz0nd Jan 5, 2026
8e2d3cd
tss_api: use user_identifier in wallet_ed25519 public_info
Ryz0nd Jan 5, 2026
dbfc749
oko_attached: remove unnecessary Size field
Ryz0nd Jan 5, 2026
76e6ba0
demo_web, sdk: add Solana signing support to demo app
Ryz0nd Jan 5, 2026
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 apps/demo_web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@oko-wallet/oko-sdk-core": "^0.0.6-rc.130",
"@oko-wallet/oko-sdk-cosmos": "^0.0.6-rc.157",
"@oko-wallet/oko-sdk-eth": "^0.0.6-rc.145",
"@oko-wallet/oko-sdk-sol": "workspace:*",
"@oko-wallet/stdlib-js": "^0.0.2-rc.44",
"@types/node": "^24.10.1",
"@types/react": "^19.2.7",
Expand Down
7 changes: 6 additions & 1 deletion apps/demo_web/src/components/oko_provider/use_oko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@ import { useSDKState } from "@oko-wallet-demo-web/state/sdk";
export function useInitOko() {
const initOkoCosmos = useSDKState((state) => state.initOkoCosmos);
const initOkoEth = useSDKState((state) => state.initOkoEth);
const initOkoSol = useSDKState((state) => state.initOkoSol);

const isInitialized = useSDKState(
(state) => state.oko_cosmos !== null && state.oko_eth !== null,
(state) =>
state.oko_cosmos !== null &&
state.oko_eth !== null &&
state.oko_sol !== null,
);

useEffect(() => {
initOkoCosmos();
initOkoEth();
initOkoSol();
}, []);

return { isInitialized };
Expand Down
13 changes: 12 additions & 1 deletion apps/demo_web/src/components/preview_panel/preview_panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,17 @@ import { CosmosOnchainSignWidget } from "@oko-wallet-demo-web/components/widgets
import { CosmosOffChainSignWidget } from "@oko-wallet-demo-web/components/widgets/cosmos_offchain_sign_widget/cosmos_offchain_sign_widget";
import { EthereumOnchainSignWidget } from "@oko-wallet-demo-web/components/widgets/ethereum_onchain_sign_widget/ethereum_onchain_sign_widget";
import { EthereumOffchainSignWidget } from "@oko-wallet-demo-web/components/widgets/ethereum_offchain_sign_widget/ethereum_offchain_sign_widget";
import { SolanaOffchainSignWidget } from "@oko-wallet-demo-web/components/widgets/solana_offchain_sign_widget/solana_offchain_sign_widget";
import { SolanaOnchainSignWidget } from "@oko-wallet-demo-web/components/widgets/solana_onchain_sign_widget/solana_onchain_sign_widget";
import { useUserInfoState } from "@oko-wallet-demo-web/state/user_info";
import { useSDKState } from "@oko-wallet-demo-web/state/sdk";

export const PreviewPanel: React.FC = () => {
const isLazyInitialized = useSDKState(
(st) => st.isCosmosLazyInitialized && st.isEthLazyInitialized,
(st) =>
st.isCosmosLazyInitialized &&
st.isEthLazyInitialized &&
st.isSolLazyInitialized,
);

const isSignedIn = useUserInfoState((state) => state.isSignedIn);
Expand All @@ -42,6 +47,12 @@ export const PreviewPanel: React.FC = () => {
<CosmosOnchainSignWidget />
</div>
)}
{isSignedIn && (
<div className={styles.col}>
<SolanaOffchainSignWidget />
<SolanaOnchainSignWidget />
</div>
)}
</>
) : (
<div className={styles.col}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { SolanaIcon } from "@oko-wallet/oko-common-ui/icons/solana_icon";

import { SignWidget } from "@oko-wallet-demo-web/components/widgets/sign_widget/sign_widget";
import { useSDKState } from "@oko-wallet-demo-web/state/sdk";

export const SolanaOffchainSignWidget = () => {
const okoSol = useSDKState((state) => state.oko_sol);

const handleClickSolOffchainSign = async () => {
if (okoSol === null) {
throw new Error("okoSol is not initialized");
}

// Connect if not already connected
if (!okoSol.connected) {
await okoSol.connect();
}

const message = "Welcome to Oko! Try generating an Ed25519 MPC signature.";
const messageBytes = new TextEncoder().encode(message);

const signature = await okoSol.signMessage(messageBytes);

// Log signature for demo purposes
console.log("Solana signature:", Buffer.from(signature).toString("hex"));
};

return (
<SignWidget
chain="Solana"
chainIcon={<SolanaIcon />}
signType="offchain"
signButtonOnClick={handleClickSolOffchainSign}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.checkboxContainer {
margin: -4px 0 20px 20px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { useCallback, useState } from "react";
import {
Connection,
PublicKey,
SystemProgram,
Transaction,
TransactionMessage,
VersionedTransaction,
LAMPORTS_PER_SOL,
} from "@solana/web3.js";
import { SolanaIcon } from "@oko-wallet/oko-common-ui/icons/solana_icon";
import { Checkbox } from "@oko-wallet/oko-common-ui/checkbox";

import styles from "./solana_onchain_sign_widget.module.scss";
import { SignWidget } from "@oko-wallet-demo-web/components/widgets/sign_widget/sign_widget";
import { useSDKState } from "@oko-wallet-demo-web/state/sdk";

const SOLANA_RPC_URL = "https://api.devnet.solana.com";

export const SolanaOnchainSignWidget = () => {
const okoSol = useSDKState((state) => state.oko_sol);
const [isLegacy, setIsLegacy] = useState(false);

const handleClickSolOnchainSignV0 = useCallback(async () => {
if (okoSol === null) {
throw new Error("okoSol is not initialized");
}

if (!okoSol.connected) {
await okoSol.connect();
}

if (!okoSol.publicKey) {
throw new Error("No public key available");
}

const connection = new Connection(SOLANA_RPC_URL);

const toAddress = new PublicKey(
"11111111111111111111111111111111",
);

const { blockhash } = await connection.getLatestBlockhash();

const instructions = [
SystemProgram.transfer({
fromPubkey: okoSol.publicKey,
toPubkey: toAddress,
lamports: 0.001 * LAMPORTS_PER_SOL,
}),
];

const messageV0 = new TransactionMessage({
payerKey: okoSol.publicKey,
recentBlockhash: blockhash,
instructions,
}).compileToV0Message();

const versionedTransaction = new VersionedTransaction(messageV0);

const signedTransaction =
await okoSol.signTransaction(versionedTransaction);

console.log(
"Solana v0 signed transaction:",
Buffer.from(signedTransaction.signatures[0]).toString("hex"),
);
}, [okoSol]);

const handleClickSolOnchainSignLegacy = useCallback(async () => {
if (okoSol === null) {
throw new Error("okoSol is not initialized");
}

if (!okoSol.connected) {
await okoSol.connect();
}

if (!okoSol.publicKey) {
throw new Error("No public key available");
}

const connection = new Connection(SOLANA_RPC_URL);

const toAddress = new PublicKey(
"11111111111111111111111111111111",
);

const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: okoSol.publicKey,
toPubkey: toAddress,
lamports: 0.001 * LAMPORTS_PER_SOL,
}),
);

const { blockhash } = await connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
transaction.feePayer = okoSol.publicKey;

const signedTransaction = await okoSol.signTransaction(transaction);

console.log(
"Solana legacy signed transaction:",
signedTransaction.signatures.map((sig) =>
sig.signature ? Buffer.from(sig.signature).toString("hex") : null,
),
);
}, [okoSol]);

return (
<SignWidget
chain="Solana"
chainIcon={<SolanaIcon />}
signType="onchain"
signButtonOnClick={
isLegacy ? handleClickSolOnchainSignLegacy : handleClickSolOnchainSignV0
}
renderBottom={() => (
<div className={styles.checkboxContainer}>
<Checkbox
size="sm"
id="set-tx-version"
checked={isLegacy}
onChange={() => setIsLegacy((prevState) => !prevState)}
label="Legacy Transaction"
/>
</div>
)}
/>
);
};
73 changes: 73 additions & 0 deletions apps/demo_web/src/state/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import {
OkoEthWallet,
type OkoEthWalletInterface,
} from "@oko-wallet/oko-sdk-eth";
import {
OkoSolWallet,
type OkoSolWalletInterface,
} from "@oko-wallet/oko-sdk-sol";
import { create } from "zustand";
import { combine } from "zustand/middleware";

Expand All @@ -14,28 +18,37 @@ import { useUserInfoState } from "@oko-wallet-demo-web/state/user_info";
interface SDKState {
oko_eth: OkoEthWalletInterface | null;
oko_cosmos: OkoCosmosWalletInterface | null;
oko_sol: OkoSolWalletInterface | null;

isEthInitializing: boolean;
isEthLazyInitialized: boolean;

isCosmosInitializing: boolean;
isCosmosLazyInitialized: boolean;

isSolInitializing: boolean;
isSolLazyInitialized: boolean;
}

interface SDKActions {
initOkoEth: () => Promise<OkoEthWalletInterface | null>;
initOkoCosmos: () => Promise<OkoCosmosWalletInterface | null>;
initOkoSol: () => Promise<OkoSolWalletInterface | null>;
}

const initialState: SDKState = {
oko_eth: null,
oko_cosmos: null,
oko_sol: null,

isEthInitializing: false,
isEthLazyInitialized: false,

isCosmosInitializing: false,
isCosmosLazyInitialized: false,

isSolInitializing: false,
isSolLazyInitialized: false,
};

export const useSDKState = create(
Expand Down Expand Up @@ -127,16 +140,76 @@ export const useSDKState = create(
return null;
}
},
initOkoSol: async () => {
const state = get();

if (state.oko_sol || state.isSolInitializing) {
console.log("Sol SDK already initialized or initializing, skipping...");
return state.oko_sol;
}

try {
console.log("Initializing Sol SDK...");
set({
isSolInitializing: true,
});

const initRes = OkoSolWallet.init({
api_key:
"72bd2afd04374f86d563a40b814b7098e5ad6c7f52d3b8f84ab0c3d05f73ac6c",
sdk_endpoint: process.env.NEXT_PUBLIC_OKO_SDK_ENDPOINT,
});

if (initRes.success) {
console.log("Sol SDK initialized");

const okoSol = initRes.data;
set({
oko_sol: okoSol,
isSolInitializing: false,
});

try {
await okoSol.waitUntilInitialized;
console.log("Sol SDK lazy initialized");
set({
isSolLazyInitialized: true,
});
} catch (e) {
console.error("Sol SDK lazy init failed:", e);
set({ isSolLazyInitialized: true }); // Still mark as done to not block
}

return okoSol;
} else {
console.error("Sol sdk init fail, err: %s", initRes.err);
set({ isSolInitializing: false, isSolLazyInitialized: true });

return null;
}
} catch (e) {
console.error("Sol SDK init error:", e);
set({ isSolInitializing: false, isSolLazyInitialized: true });
return null;
}
},
})),
);

function setupCosmosListener(cosmosSDK: OkoCosmosWalletInterface) {
const setUserInfo = useUserInfoState.getState().setUserInfo;

if (cosmosSDK) {
console.log("[Demo] Setting up Cosmos accountsChanged listener");
cosmosSDK.on({
type: "accountsChanged",
handler: ({ authType, email, publicKey, name }) => {
console.log("[Demo] accountsChanged event received:", {
authType,
email,
publicKey: publicKey ? "exists" : "null",
name,
});
setUserInfo({
authType: authType || null,
email: email || null,
Expand Down
18 changes: 18 additions & 0 deletions apps/docs_web/docs/v0/sdk-usage/sdk-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ npm install @oko-wallet/oko-sdk-cosmos
# For Ethereum/EVM chains
npm install @oko-wallet/oko-sdk-eth

# For Solana
npm install @oko-wallet/oko-sdk-sol

# Core SDK (for custom integration)
npm install @oko-wallet/oko-sdk-core
```
Expand Down Expand Up @@ -56,10 +59,25 @@ const ethWallet = initRes.data;
const provider = await ethWallet.getEthereumProvider();
```

### Solana

```typescript
import { OkoSolWallet } from "@oko-wallet/oko-sdk-sol";

const initRes = OkoSolWallet.init(config);
if (!initRes.success) {
throw new Error(`Solana wallet initialization failed: ${initRes.err}`);
}

const solWallet = initRes.data;
await solWallet.connect();
```

## Next Steps

- **[Cosmos Integration](./cosmos-integration)** - Complete Cosmos setup
- **[Ethereum Integration](./ethereum-integration)** - Complete Ethereum setup
- **[Solana Integration](./solana-integration)** - Complete Solana setup
- **[React Integration](./react-integration)** - React patterns
- **[RainbowKit Integration](./rainbow-kit-integration)** - RainbowKit
integration
Expand Down
Loading