Last active
April 20, 2025 11:42
-
-
Save AlmostEfficient/fb0ea2e867a7b3e48caff69ea008c5fd to your computer and use it in GitHub Desktop.
express server service to create and fetch turnkey solana wallets
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Turnkey } from '@turnkey/sdk-server'; | |
import { logger } from '../utils/logger.js'; | |
import fetch from 'node-fetch'; | |
const turnkey = new Turnkey({ | |
apiBaseUrl: process.env.TURNKEY_API_BASE_URL || 'https://api.turnkey.com', | |
apiPrivateKey: process.env.TURNKEY_API_PRIVATE_KEY, | |
apiPublicKey: process.env.TURNKEY_API_PUBLIC_KEY, | |
defaultOrganizationId: process.env.TURNKEY_ORGANIZATION_ID, | |
}); | |
/** | |
* Create a sub-organization with a Solana wallet for a user | |
* @param {string} email - User's email | |
* @returns {Promise<Object>} - Sub-organization and wallet details | |
*/ | |
export const createSubOrganization = async (email) => { | |
try { | |
const apiClient = turnkey.apiClient(); | |
const subOrganizationConfig = { | |
subOrganizationName: `${email}'s Forma Wallet`, | |
rootUsers: [{ | |
userName: email, | |
userEmail: email, | |
apiKeys: [], | |
authenticators: [], | |
oauthProviders: [] | |
}], | |
rootQuorumThreshold: 1, | |
wallet: { | |
walletName: "Forma Solana Wallet", | |
accounts: [ | |
{ | |
curve: "CURVE_ED25519", // Solana uses Ed25519 | |
pathFormat: "PATH_FORMAT_BIP32", | |
path: "m/44'/501'/0'/0'", // Solana's BIP44 path | |
addressFormat: "ADDRESS_FORMAT_SOLANA" | |
} | |
] | |
} | |
}; | |
logger.info(`Creating sub-organization for email: ${email}`); | |
const response = await apiClient.createSubOrganization(subOrganizationConfig); | |
logger.info(`Sub-organization created with ID: ${response.subOrganizationId}`); | |
return { | |
subOrganizationId: response.subOrganizationId, | |
wallet: { | |
walletId: response.wallet.walletId, | |
addresses: response.wallet.addresses | |
} | |
}; | |
} catch (error) { | |
logger.error(`Error creating sub-organization: ${error.message}`); | |
throw error; | |
} | |
}; | |
/** | |
* Get sub-organizations and wallet addresses for a user by email | |
* @param {string} email - User's email | |
* @returns {Promise<Array>} - List of sub-organizations and their wallets | |
*/ | |
export const getUserSubOrganizations = async (email) => { | |
try { | |
logger.info(`Fetching organizations for email: ${email}`); | |
const apiClient = turnkey.apiClient(); | |
const response = await apiClient.getSubOrgIds({ | |
organizationId: process.env.TURNKEY_ORGANIZATION_ID, | |
filterType: "EMAIL", | |
filterValue: email | |
}); | |
console.log('getSubOrgIds response:', JSON.stringify(response, null, 2)); | |
const userSubOrgs = []; | |
for (const orgId of response.organizationIds) { | |
console.log(`\nFetching wallets for organization: ${orgId}`); | |
const walletsResponse = await apiClient.getWallets({ | |
organizationId: orgId | |
}); | |
console.log('getWallets response:', JSON.stringify(walletsResponse, null, 2)); | |
const wallets = walletsResponse.wallets.map(wallet => ({ | |
walletId: wallet.walletId, | |
walletName: wallet.walletName, | |
addresses: wallet.addresses | |
})); | |
console.log('Processed wallet data:', JSON.stringify(wallets, null, 2)); | |
userSubOrgs.push({ | |
subOrganizationId: orgId, | |
wallets | |
}); | |
} | |
console.log('Final response:', JSON.stringify(userSubOrgs, null, 2)); | |
return userSubOrgs; | |
} catch (error) { | |
console.error('Detailed error:', error); | |
logger.error(`Error fetching organizations: ${error.message}`); | |
throw error; | |
} | |
}; | |
/** | |
* Gathers comprehensive debug information for a user by email. | |
* Logs details about the user, their sub-organizations, wallets, and accounts. | |
* @param {string} email - User's email | |
* @returns {Promise<void>} | |
*/ | |
export const debugUserInfo = async (email) => { | |
const apiClient = turnkey.apiClient(); | |
const rootOrganizationId = process.env.TURNKEY_ORGANIZATION_ID; | |
console.log(`\n--- Debugging user: ${email} ---`); | |
console.log(`--- Using Organization ID: ${rootOrganizationId} ---`); | |
// Helper to log errors | |
const logError = (context, error) => { | |
console.error(`[${context}] Error:`, error?.message || error); | |
if (error?.details) { | |
console.error(`[${context}] Error Details:`, JSON.stringify(error.details, null, 2)); | |
} | |
}; | |
try { | |
// 1. Get User details in the root organization | |
console.log(`\n[Root Org: ${rootOrganizationId}] Fetching users...`); | |
const rootUsers = await apiClient.getUsers({ | |
organizationId: rootOrganizationId, | |
}); | |
console.log(`[Root Org: ${rootOrganizationId}] getUsers response:`, JSON.stringify(rootUsers, null, 2)); | |
const matchingRootUsers = rootUsers.users.filter(user => user.userEmail === email); | |
if (matchingRootUsers.length > 0) { | |
console.log(`[Root Org: ${rootOrganizationId}] Found matching user(s) in root org:`, JSON.stringify(matchingRootUsers, null, 2)); | |
} else { | |
console.log(`[Root Org: ${rootOrganizationId}] No user found with email ${email} in root org.`); | |
} | |
} catch (error) { | |
logError(`Root Org: ${rootOrganizationId} - Fetching Users`, error); | |
} | |
let subOrgIds = []; | |
try { | |
// 2. Get Sub-Organization IDs | |
console.log(`\n[Org ID: ${rootOrganizationId}] Fetching sub-org IDs for email: ${email}`); | |
const subOrgResponse = await apiClient.getSubOrgIds({ | |
organizationId: rootOrganizationId, | |
filterType: "EMAIL", | |
filterValue: email | |
}); | |
console.log(`[Org ID: ${rootOrganizationId}] getSubOrgIds response:`, JSON.stringify(subOrgResponse, null, 2)); | |
subOrgIds = subOrgResponse.organizationIds || []; | |
if (subOrgIds.length > 0) { | |
console.log(`[Org ID: ${rootOrganizationId}] Found ${subOrgIds.length} sub-organization(s).`); | |
} else { | |
console.log(`[Org ID: ${rootOrganizationId}] No sub-organizations found.`); | |
} | |
} catch (error) { | |
logError(`Fetching Sub-Org IDs`, error); | |
} | |
// 3. Iterate through found sub-organizations | |
for (const subOrgId of subOrgIds) { | |
console.log(`\n--- Processing Sub-Organization ID: ${subOrgId} ---`); | |
try { | |
// 3a. Get Sub-Organization details | |
console.log(`[Sub Org: ${subOrgId}] Fetching organization details...`); | |
const orgDetails = await apiClient.getOrganization({ organizationId: subOrgId }); | |
console.log(`[Sub Org: ${subOrgId}] getOrganization response:`, JSON.stringify(orgDetails, null, 2)); | |
} catch (error) { | |
logError(`Sub Org: ${subOrgId} - Fetching Details`, error); | |
} | |
try { | |
// 3b. Get Users within the sub-organization | |
console.log(`[Sub Org: ${subOrgId}] Fetching users...`); | |
const subOrgUsers = await apiClient.getUsers({ organizationId: subOrgId }); | |
console.log(`[Sub Org: ${subOrgId}] getUsers response:`, JSON.stringify(subOrgUsers, null, 2)); | |
const matchingSubOrgUsers = subOrgUsers.users.filter(user => user.userEmail === email); | |
if (matchingSubOrgUsers.length > 0) { | |
console.log(`[Sub Org: ${subOrgId}] Found matching user(s):`, JSON.stringify(matchingSubOrgUsers, null, 2)); | |
} else { | |
console.log(`[Sub Org: ${subOrgId}] No user found with email ${email} in this sub-org.`); | |
} | |
} catch (error) { | |
logError(`Sub Org: ${subOrgId} - Fetching Users`, error); | |
} | |
let walletIds = []; | |
try { | |
// 3c. Get Wallets within the sub-organization | |
console.log(`[Sub Org: ${subOrgId}] Fetching wallets...`); | |
const walletsResponse = await apiClient.getWallets({ organizationId: subOrgId }); | |
console.log(`[Sub Org: ${subOrgId}] getWallets response:`, JSON.stringify(walletsResponse, null, 2)); | |
walletIds = walletsResponse.wallets.map(w => w.walletId); | |
if (walletIds.length === 0) { | |
console.log(`[Sub Org: ${subOrgId}] No wallets found in this sub-organization.`); | |
} | |
} catch (error) { | |
logError(`Sub Org: ${subOrgId} - Fetching Wallets`, error); | |
} | |
// 4. Iterate through found wallets | |
for (const walletId of walletIds) { | |
console.log(`\n--- [Sub Org: ${subOrgId}] Processing Wallet ID: ${walletId} ---`); | |
try { | |
// 4a. Get specific Wallet details | |
console.log(`[Wallet: ${walletId}] Fetching wallet details...`); | |
const walletDetails = await apiClient.getWallet({ organizationId: subOrgId, walletId: walletId }); | |
console.log(`[Wallet: ${walletId}] getWallet response:`, JSON.stringify(walletDetails, null, 2)); | |
} catch (error) { | |
logError(`Wallet: ${walletId} - Fetching Details`, error); | |
} | |
try { | |
// 4b. Get Wallet Accounts | |
console.log(`[Wallet: ${walletId}] Fetching wallet accounts...`); | |
const walletAccounts = await apiClient.getWalletAccounts({ organizationId: subOrgId, walletId: walletId }); | |
console.log(`[Wallet: ${walletId}] getWalletAccounts response:`, JSON.stringify(walletAccounts, null, 2)); | |
if (!walletAccounts.accounts || walletAccounts.accounts.length === 0) { | |
console.log(`[Wallet: ${walletId}] No accounts found in this wallet.`); | |
} | |
} catch (error) { | |
logError(`Wallet: ${walletId} - Fetching Accounts`, error); | |
} | |
} | |
} | |
console.log(`\n--- Debugging finished for user: ${email} ---`); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment