UNPKG

@osiris-ai/turnkey-sdk

Version:
395 lines (390 loc) 14.1 kB
import { ApiKeyStamper } from '@turnkey/api-key-stamper'; import axios from 'axios'; import { WalletAuthenticator, AuthenticatorType } from '@osiris-ai/sdk'; // @osiris-ai/turnkey-sdk - Turnkey Integration SDK // src/types.ts var getChainIdFromAddressFormat = (addressFormat) => { switch (addressFormat) { case "ADDRESS_FORMAT_ETHEREUM": return "evm:eip155:1"; case "ADDRESS_FORMAT_SOLANA": return "solana:mainnet-beta"; default: return "evm:eip155:1"; } }; var TurnkeyAuthenticator = class extends WalletAuthenticator { sdk; constructor(config) { super("turnkey", config); this.sdk = new TurnkeySDK(config); } async getWallet(params) { const walletAccountsResponse = await this.sdk.listWalletAccounts({ organizationId: params.id, walletId: params.accountId, paginationOptions: { limit: "100", before: "", after: "" } }); const account = { id: params.id, addresses: walletAccountsResponse.accounts.map((account2) => ({ chains: [getChainIdFromAddressFormat(account2.addressFormat)], address: account2.address, derivationPath: account2.path, curve: account2.curve, addressFormat: account2.addressFormat })), chains: [getChainIdFromAddressFormat(walletAccountsResponse.accounts[0].addressFormat)], mnemonicLength: 24 }; return { id: params.accountId, name: "", accounts: account, authenticators: [] }; } async addAccount(params) { const addAccountsResponse = await this.sdk.addWalletAccount({ type: "ACTIVITY_TYPE_CREATE_WALLET_ACCOUNTS", timestampMs: Date.now().toString(), organizationId: params.walletId, parameters: { walletId: params.accountId, accounts: params.addresses.map((acc) => ({ pathFormat: acc.pathFormat, path: acc.path, curve: acc.curve, addressFormat: acc.addressFormat })) } }); if (!addAccountsResponse) { throw new Error("Failed to add account"); } return this.getWallet({ id: params.walletId, accountId: params.accountId }); } async createWallet(params) { const turnkeyRequest = { subOrganizationName: params.name, rootUsers: [ { userName: params.name, apiKeys: params.authenticators.filter((authenticator) => authenticator.type === AuthenticatorType.API_KEY).map((authenticator) => ({ apiKeyName: authenticator.credentials.apiKeyName, publicKey: authenticator.credentials.publicKey, curveType: authenticator.credentials.curveType })) ?? [], authenticators: params.authenticators.filter((authenticator) => authenticator.type === AuthenticatorType.WEBAUTHN).map((authenticator) => ({ authenticatorName: authenticator.credentials.authenticatorName, challenge: authenticator.credentials.challenge, attestation: authenticator.credentials.attestation })) ?? [], oauthProviders: params.authenticators.filter((authenticator) => authenticator.type === AuthenticatorType.OAUTH).map((authenticator) => ({ oauthProviderName: authenticator.credentials.oauthProviderName, oidcToken: authenticator.credentials.oidcToken })) ?? [] } ], rootQuorumThreshold: 1, wallet: { walletName: params.name, accounts: params.accounts.map((acc) => ({ pathFormat: acc.pathFormat, path: acc.path, curve: acc.curve, addressFormat: acc.addressFormat })), mnemonicLength: 12 } }; const response = await this.sdk.createWallet(turnkeyRequest); const responseAddresses = response.activity.result.createSubOrganizationResultV7.wallet.addresses; const allChains = Array.from(new Set(params.accounts.flatMap((acc) => acc.chains))); const addresses = params.accounts.map((acc, idx) => ({ chains: acc.chains, address: responseAddresses && responseAddresses[idx] ? responseAddresses[idx] : "", derivationPath: acc.path, curve: acc.curve, addressFormat: acc.addressFormat })); const account = { id: response.activity.result.createSubOrganizationResultV7.wallet.walletId, addresses, chains: allChains, mnemonicLength: 24 }; return { id: response.activity.result.createSubOrganizationResultV7.subOrganizationId, name: params.name, accounts: account, authenticators: [] // TODO: Not available from Turnkey }; } async action(params) { if (!params.data) { throw new Error("No data provided"); } const { method, walletId, walletAddress, payload, chain } = params.data; if (!method || !walletId || !walletAddress || !payload) { throw new Error(`Invalid parameters, missing ${!method ? "method" : !walletId ? "walletId" : !walletAddress ? "walletAddress" : "payload"}`); } try { switch (method) { case "signMessage": return await this.handleSignMessage(walletId, walletAddress, payload); case "signTypedData": return await this.handleSignTypedData(walletId, walletAddress, payload); case "signTransaction": return await this.handleSignTransaction( walletId, walletAddress, payload, chain ); case "signRawPayloads": return await this.handleSignRawPayloads(walletId, walletAddress, payload); default: throw new Error(`Unsupported method: ${method}`); } } catch (error) { console.error(`Action execution failed:`, error.message); throw new Error(`Failed to execute ${method}: ${error.response ? JSON.stringify(error.response.data) : error.message}`); } } async handleSignMessage(walletId, walletAddress, payload) { const tx = await this.sdk.signRawPayloads({ organizationId: walletId, timestampMs: Date.now().toString(), type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOADS", parameters: { signWith: walletAddress, payloads: Array.isArray(payload) ? payload : [payload], encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_NO_OP" } }); if (tx.activity.status === "ACTIVITY_STATUS_COMPLETED") { return { data: tx.activity.result.signRawPayloadsResult, status: 200, statusText: "OK" }; } throw new Error("Failed to sign message"); } async handleSignTypedData(walletId, walletAddress, payload) { let typedData = payload; if (Array.isArray(payload)) { if (payload.length === 0) { throw new Error("Empty payload array provided for typed data signing"); } typedData = payload[0]; } if (!typedData.domain || !typedData.types || !typedData.primaryType || !typedData.message) { throw new Error("Invalid EIP-712 typed data structure: missing required fields (domain, types, primaryType, message)"); } const typedDataString = JSON.stringify(typedData); const tx = await this.sdk.signRawPayload({ organizationId: walletId, timestampMs: Date.now().toString(), type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2", parameters: { signWith: walletAddress, payload: typedDataString, encoding: "PAYLOAD_ENCODING_EIP712", hashFunction: "HASH_FUNCTION_NO_OP" } }); if (tx.activity.status === "ACTIVITY_STATUS_COMPLETED") { return { data: { signatures: [tx.activity.result.signRawPayloadResult], originalTypedData: typedData }, status: 200, statusText: "OK" }; } throw new Error("Failed to sign typed data"); } async handleSignTransaction(walletId, walletAddress, payload, chain) { if (!payload.serializedTransaction) { throw new Error("Missing serializedTransaction in payload"); } const transactionType = chain?.startsWith("evm") ? "TRANSACTION_TYPE_ETHEREUM" : "TRANSACTION_TYPE_SOLANA"; const tx = await this.sdk.signTransaction({ type: "ACTIVITY_TYPE_SIGN_TRANSACTION_V2", timestampMs: Date.now().toString(), organizationId: walletId, parameters: { signWith: walletAddress, unsignedTransaction: payload.serializedTransaction, type: transactionType } }); if (tx.activity.status === "ACTIVITY_STATUS_COMPLETED") { return { data: tx.activity.result.signTransactionResult, status: 200, statusText: "OK" }; } throw new Error("Failed to sign transaction"); } async handleSignRawPayloads(walletId, walletAddress, payload) { const payloads = Array.isArray(payload) ? payload : [payload]; const tx = await this.sdk.signRawPayloads({ organizationId: walletId, timestampMs: Date.now().toString(), type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOADS", parameters: { signWith: walletAddress, payloads, encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_NO_OP" } }); if (tx.activity.status === "ACTIVITY_STATUS_COMPLETED") { return { data: tx.activity.result.signRawPayloadsResult, status: 200, statusText: "OK" }; } throw new Error("Failed to sign raw payloads"); } }; // src/index.ts var TurnkeySDK = class { constructor(config) { this.config = config; this.stamper = new ApiKeyStamper({ apiPublicKey: this.config.apiPublicKey, apiPrivateKey: this.config.apiPrivateKey }); } stamper; async listWallets(request) { const requestBody = request; const stamp = await this.stamper.stamp(JSON.stringify(requestBody)); const response = await axios.post( `${this.config.baseUrl}/public/v1/query/list_wallets`, requestBody, { headers: { [stamp.stampHeaderName]: stamp.stampHeaderValue } } ); return response.data; } async listWalletAccounts(request) { const requestBody = request; const stamp = await this.stamper.stamp(JSON.stringify(requestBody)); const response = await axios.post( `${this.config.baseUrl}/public/v1/query/list_wallet_accounts`, requestBody, { headers: { [stamp.stampHeaderName]: stamp.stampHeaderValue } } ); return response.data; } async createWallet(request) { const requestBody = { type: "ACTIVITY_TYPE_CREATE_SUB_ORGANIZATION_V7", timestampMs: (/* @__PURE__ */ new Date()).getTime().toString(), organizationId: this.config.organizationId, parameters: { subOrganizationName: request.subOrganizationName, rootUsers: request.rootUsers.map((rootUser) => ({ userName: rootUser.userName, apiKeys: rootUser.apiKeys, authenticators: rootUser.authenticators, oauthProviders: rootUser.oauthProviders })), rootQuorumThreshold: request.rootQuorumThreshold, wallet: { walletName: request.wallet.walletName, accounts: request.wallet.accounts.map((account) => ({ pathFormat: account.pathFormat, path: account.path, curve: account.curve, addressFormat: account.addressFormat })), mnemonicLength: request.wallet.mnemonicLength } } }; const stamp = await this.stamper.stamp(JSON.stringify(requestBody)); try { const response = await axios.post( `${this.config.baseUrl}/public/v1/submit/create_sub_organization`, requestBody, { headers: { [stamp.stampHeaderName]: stamp.stampHeaderValue } } ); return response.data; } catch (error) { console.log(error.response.data); throw new Error("Failed to create wallet"); } } async addWalletAccount(request) { try { console.log(JSON.stringify(request, null, 2)); const stamp = await this.stamper.stamp(JSON.stringify(request)); const response = await axios.post( `${this.config.baseUrl}/public/v1/submit/create_wallet_accounts`, request, { headers: { [stamp.stampHeaderName]: stamp.stampHeaderValue } } ); return response.data; } catch (error) { console.log(error.response.data); if (axios.isAxiosError(error)) { throw new Error(`Axios error: ${error.response?.status} ${error.response?.statusText} - ${JSON.stringify(error.response?.data)}`); } throw new Error("Failed to add wallet account"); } } async signRawPayload(request) { const stamp = await this.stamper.stamp(JSON.stringify(request)); const response = await axios.post( `${this.config.baseUrl}/public/v1/submit/sign_raw_payload`, request, { headers: { [stamp.stampHeaderName]: stamp.stampHeaderValue } } ); return response.data; } async signRawPayloads(request) { try { const stamp = await this.stamper.stamp(JSON.stringify(request)); const response = await axios.post( `${this.config.baseUrl}/public/v1/submit/sign_raw_payloads`, request, { headers: { [stamp.stampHeaderName]: stamp.stampHeaderValue } } ); return response.data; } catch (error) { if (axios.isAxiosError(error)) { throw new Error(`Axios error: ${error.response?.status} ${error.response?.statusText} - ${JSON.stringify(error.response?.data)}`); } throw new Error("Failed to sign raw payloads"); } } async signTransaction(request) { const stamp = await this.stamper.stamp(JSON.stringify(request)); const response = await axios.post( `${this.config.baseUrl}/public/v1/submit/sign_transaction`, request, { headers: { [stamp.stampHeaderName]: stamp.stampHeaderValue } } ); return response.data; } }; export { TurnkeyAuthenticator, TurnkeySDK, getChainIdFromAddressFormat }; //# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map