Created
March 19, 2025 06:45
-
-
Save AlmostEfficient/8aae477125dd253413bdd1ee79731adc to your computer and use it in GitHub Desktop.
Create a non-custodial Solana wallet on Turnkey with a Supabase edge function
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 { serve } from "https://deno.land/[email protected]/http/server.ts"; | |
import { Turnkey } from "npm:@turnkey/sdk-server"; | |
import { corsHeaders } from "../_shared/cors.ts"; | |
const turnkey = new Turnkey({ | |
apiBaseUrl: "https://api.turnkey.com", | |
apiPrivateKey: Deno.env.get("TURNKEY_API_PRIVATE_KEY")!, | |
apiPublicKey: Deno.env.get("TURNKEY_API_PUBLIC_KEY")!, | |
defaultOrganizationId: Deno.env.get("TURNKEY_ORGANIZATION_ID")!, | |
}); | |
type CreateSubOrgResponse = { | |
subOrganizationId: string; | |
wallet: { | |
walletId: string; | |
addresses: string[]; | |
}; | |
}; | |
async function createSubOrganization(email: string): Promise<CreateSubOrgResponse> { | |
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" | |
} | |
] | |
} | |
}; | |
const response = await apiClient.createSubOrganization(subOrganizationConfig); | |
return { | |
subOrganizationId: response.subOrganizationId, | |
wallet: { | |
walletId: response.wallet.walletId, | |
addresses: response.wallet.addresses | |
} | |
}; | |
} | |
serve(async (req) => { | |
// Handle CORS | |
if (req.method === "OPTIONS") { | |
return new Response("ok", { headers: corsHeaders }); | |
} | |
try { | |
// Step 1: Parse and validate request | |
const { email } = await req.json() as { email: string }; | |
if (!email || !email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) { | |
throw new Error("Invalid email format"); | |
} | |
console.log('✓ Email validation passed:', email) | |
// Step 2: Test connection by getting organization ID | |
const organizationId = Deno.env.get("TURNKEY_ORGANIZATION_ID"); | |
if (!organizationId) { | |
throw new Error("TURNKEY_ORGANIZATION_ID not set"); | |
} | |
console.log('✓ Turnkey client initialized with organization:', organizationId) | |
// Create sub-organization for the user | |
const subOrgResult = await createSubOrganization(email); | |
console.log('✓ Created sub-organization:', subOrgResult.subOrganizationId); | |
// Return success response | |
return new Response( | |
JSON.stringify({ | |
success: true, | |
message: "Sub-organization created successfully", | |
email, | |
organizationId, | |
subOrganizationId: subOrgResult.subOrganizationId, | |
walletId: subOrgResult.wallet.walletId, | |
addresses: subOrgResult.wallet.addresses | |
}), | |
{ | |
headers: { | |
...corsHeaders, | |
'Content-Type': 'application/json' | |
}, | |
status: 200 | |
} | |
); | |
} catch (error) { | |
console.error('Error:', error.message) | |
return new Response( | |
JSON.stringify({ | |
success: false, | |
error: error.message | |
}), | |
{ | |
headers: { | |
...corsHeaders, | |
'Content-Type': 'application/json' | |
}, | |
status: error.message.includes("Invalid email") ? 400 : 500 | |
} | |
); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment