@shogun-sdk/accounts
Version:
Shogun with Turnkey: configs, encryption, authentication with Telegram/Turnkey OIDC, etc.
192 lines • 7.29 kB
JavaScript
import { ApiKeyStamper, DEFAULT_ETHEREUM_ACCOUNTS, DEFAULT_SOLANA_ACCOUNTS, TurnkeyServerClient, } from '@turnkey/sdk-server';
import { decode } from 'jsonwebtoken';
import { TURNKEY_APP_CONFIG } from '../config/index.js';
export const TURNKEY_SESSION_EXPIRE = '7200'; // 2 hours in seconds
export const getTurnkeyConfig = (baseUrl, organizationId, rpId) => ({
apiBaseUrl: baseUrl,
defaultOrganizationId: organizationId,
rpId,
iframeUrl: 'https://auth.turnkey.com',
});
export const TURNKEY_EMBEDDED_KEY = "@turnkey/embedded_key";
export const TURNKEY_EMBEDDED_KEY_TTL_IN_MILLIS = 1000 * 60 * 60 * 24 * 365 * 10; // 10 years in milliseconds
export const TURNKEY_CREDENTIAL_BUNDLE = "@turnkey/credential_bundle";
export const TURNKEY_CREDENTIAL_BUNDLE_TTL_IN_MILLIS = 1000 * 60 * 60 * 24 * 365 * 10; // 48 hours in milliseconds;
export const getApiKeyStamper = (apiPublicKey, apiPrivateKey) => new ApiKeyStamper({
apiPublicKey,
apiPrivateKey,
});
const getTurnkeyClient = (baseUrl, organizationId, stamper) => new TurnkeyServerClient({
apiBaseUrl: baseUrl,
organizationId,
stamper,
});
function decodeJwt(credential) {
const decoded = decode(credential);
if (decoded && typeof decoded === 'object' && 'email' in decoded) {
return decoded;
}
return null;
}
export const oauth = async ({ credential, targetPublicKey, targetSubOrgId, config, stamper, }) => {
const client = getTurnkeyClient(config.apiBaseUrl, config.defaultOrganizationId, stamper);
const oauthResponse = await client.oauth({
oidcToken: credential,
targetPublicKey,
organizationId: targetSubOrgId,
expirationSeconds: TURNKEY_SESSION_EXPIRE,
});
return oauthResponse;
};
export async function getSubOrgId(config, param, stamper) {
let filterType;
let filterValue;
if ('userName' in param) {
filterType = 'USERNAME';
filterValue = param.userName;
}
else if ('email' in param) {
filterType = 'EMAIL';
filterValue = param.email;
}
else if ('publicKey' in param) {
filterType = 'PUBLIC_KEY';
filterValue = param.publicKey;
}
else if ('oidcToken' in param) {
filterType = 'OIDC_TOKEN';
filterValue = param.oidcToken;
}
else {
throw new Error('Invalid parameter');
}
const client = getTurnkeyClient(config.apiBaseUrl, config.defaultOrganizationId, stamper);
const { organizationIds } = await client.getSubOrgIds({
organizationId: config.defaultOrganizationId,
filterType,
filterValue,
});
return organizationIds[0];
}
export const getSubOrgIdByEmail = async (config, email, stamper) => {
return getSubOrgId(config, { email }, stamper);
};
export const createUserSubOrg = async ({ config, username, email, passkey, oauth, delegationApiKey, onSuccess, stamper, }) => {
const authenticators = passkey
? [
{
authenticatorName: 'Passkey',
challenge: passkey.challenge,
attestation: passkey.attestation,
},
]
: [];
const oauthProviders = oauth
? [
{
providerName: oauth.providerName,
oidcToken: oauth.oidcToken,
},
]
: [];
let userEmail = email;
// If the user is logging in with a Google Auth credential, use the email from the decoded OIDC token (credential
// Otherwise, use the email from the email parameter
if (oauth) {
const decoded = decodeJwt(oauth.oidcToken);
if (decoded?.email) {
userEmail = decoded.email;
}
}
const subOrganizationName = `Sub Org - ${email ?? ''}`;
const userName = username ? username : email ? email.split('@')?.[0] || email : '';
const client = getTurnkeyClient(config.apiBaseUrl, config.defaultOrganizationId, stamper);
const subOrg = await client.createSubOrganization({
organizationId: config.defaultOrganizationId,
subOrganizationName,
rootUsers: [
{
userName,
userEmail,
oauthProviders,
authenticators,
apiKeys: delegationApiKey ? [{
apiKeyName: TURNKEY_APP_CONFIG.BOT_DELEGATION_NAME,
publicKey: delegationApiKey,
curveType: 'API_KEY_CURVE_P256',
}] : [],
},
],
rootQuorumThreshold: 1,
wallet: {
walletName: 'Default Wallet',
accounts: [...DEFAULT_ETHEREUM_ACCOUNTS, ...DEFAULT_SOLANA_ACCOUNTS],
},
});
const userId = subOrg.rootUserIds?.[0];
if (!userId) {
throw new Error('No root user ID found');
}
const { user } = await client.getUser({
organizationId: subOrg.subOrganizationId,
userId,
});
if (delegationApiKey && onSuccess) {
await onSuccess?.(subOrg, delegationApiKey);
}
return { subOrg, user };
};
export const getSubOrgIdByPublicKey = async (config, publicKey, stamper) => {
return getSubOrgId(config, { publicKey }, stamper);
};
export const getSubOrgIdByUserName = async (config, userName, stamper) => {
return getSubOrgId(config, { userName: userName.toLowerCase() }, stamper);
};
export async function getWalletsWithAccounts(config, organizationId, stamper) {
try {
const client = getTurnkeyClient(config.apiBaseUrl, config.defaultOrganizationId, stamper);
const { wallets } = await client.getWallets({
organizationId,
});
const walletsWithAccounts = await Promise.all(wallets.map(async (wallet) => {
try {
const { accounts } = await client.getWalletAccounts({
organizationId,
walletId: wallet.walletId,
});
return { ...wallet, accounts };
}
catch (error) {
console.error(`Failed to fetch accounts for wallet ${wallet.walletId}:`, error);
// Return wallet with empty accounts array if account fetch fails
return { ...wallet, accounts: [] };
}
}));
return walletsWithAccounts;
}
catch (error) {
console.error('Failed to fetch wallets:', error);
throw new Error('Failed to fetch wallets and accounts. Please try again later.');
}
}
export async function getUserOrganization(config, organizationId, stamper) {
const client = getTurnkeyClient(config.apiBaseUrl, config.defaultOrganizationId, stamper);
const data = await client.getOrganization({
organizationId,
});
return data;
}
export async function initEmailRecovery(config, organizationId, email, targetPublicKey, stamper) {
const client = getTurnkeyClient(config.apiBaseUrl, config.defaultOrganizationId, stamper);
const data = await client.initUserEmailRecovery({
organizationId,
email: email,
targetPublicKey: targetPublicKey,
emailCustomization: {
appName: 'Gun.fun',
logoUrl: 'https://www.gun.fun/images/logo.svg',
},
});
return data;
}
//# sourceMappingURL=turnkey.js.map