UNPKG

@turnkey/viem

Version:

Turnkey Helpers to work with Viem

275 lines (272 loc) 10.7 kB
import { BaseError, isAddress, serializeTransaction, hashMessage, hashTypedData, signatureToHex } from 'viem'; import { toAccount } from 'viem/accounts'; import { TurnkeyActivityError as TurnkeyActivityError$1, TurnkeyClient, TurnkeyActivityConsensusNeededError, assertActivityCompleted, assertNonNull } from '@turnkey/http'; import { ApiKeyStamper } from '@turnkey/api-key-stamper'; class TurnkeyConsensusNeededError extends BaseError { constructor({ message = "Turnkey activity requires consensus.", activityId, activityStatus, }) { super(message); this.name = "TurnkeyConsensusNeededError"; this.activityId = activityId; this.activityStatus = activityStatus; } } class TurnkeyActivityError extends BaseError { constructor({ message = "Received unexpected Turnkey activity status.", activityId, activityStatus, }) { super(message); this.name = "TurnkeyActivityError"; this.activityId = activityId; this.activityStatus = activityStatus; } } function createAccountWithAddress(input) { const { client, organizationId, signWith } = input; let { ethereumAddress } = input; if (!signWith) { throw new TurnkeyActivityError$1({ message: `Missing signWith parameter`, }); } if (isAddress(signWith)) { // override provided `ethereumAddress` ethereumAddress = signWith; } else if (!ethereumAddress) { throw new TurnkeyActivityError({ message: `Missing ethereumAddress parameter`, }); } return toAccount({ address: ethereumAddress, signMessage: function ({ message, }) { return signMessage(client, message, organizationId, signWith); }, signTransaction: function (transaction, options) { const serializer = options?.serializer ?? serializeTransaction; return signTransaction(client, transaction, serializer, organizationId, signWith); }, signTypedData: function (typedData) { return signTypedData(client, typedData, organizationId, signWith); }, }); } async function createAccount(input) { const { client, organizationId, signWith } = input; let { ethereumAddress } = input; if (!signWith) { throw new TurnkeyActivityError({ message: `Missing signWith parameter`, }); } if (isAddress(signWith)) { // override provided `ethereumAddress` ethereumAddress = signWith; } else if (!ethereumAddress) { // we have a private key ID, but not an ethereumAddress const data = await client.getPrivateKey({ privateKeyId: signWith, organizationId: organizationId, }); ethereumAddress = data.privateKey.addresses.find((item) => item.format === "ADDRESS_FORMAT_ETHEREUM")?.address; if (typeof ethereumAddress !== "string" || !ethereumAddress) { throw new TurnkeyActivityError({ message: `Unable to find Ethereum address for key ${signWith} under organization ${organizationId}`, }); } } return createAccountWithAddress({ client, organizationId, signWith, ethereumAddress, }); } /** * Creates a new Custom Account backed by a Turnkey API key. * @deprecated use {@link createAccount} instead. */ async function createApiKeyAccount(config) { const { apiPublicKey, apiPrivateKey, baseUrl, organizationId, privateKeyId } = config; const stamper = new ApiKeyStamper({ apiPublicKey: apiPublicKey, apiPrivateKey: apiPrivateKey, }); const client = new TurnkeyClient({ baseUrl: baseUrl, }, stamper); const data = await client.getPrivateKey({ privateKeyId: privateKeyId, organizationId: organizationId, }); const ethereumAddress = data.privateKey.addresses.find((item) => item.format === "ADDRESS_FORMAT_ETHEREUM")?.address; if (typeof ethereumAddress !== "string" || !ethereumAddress) { throw new TurnkeyActivityError$1({ message: `Unable to find Ethereum address for key ${privateKeyId} under organization ${organizationId}`, }); } return toAccount({ address: ethereumAddress, signMessage: function ({ message, }) { return signMessage(client, message, organizationId, privateKeyId); }, signTransaction: function (transaction, options) { const serializer = options?.serializer ?? serializeTransaction; return signTransaction(client, transaction, serializer, organizationId, privateKeyId); }, signTypedData: function (typedData) { return signTypedData(client, typedData, organizationId, privateKeyId); }, }); } async function signMessage(client, message, organizationId, signWith) { const hashedMessage = hashMessage(message); const signedMessage = await signMessageWithErrorWrapping(client, hashedMessage, organizationId, signWith); return `${signedMessage}`; } async function signTransaction(client, transaction, serializer, organizationId, signWith) { const serializedTx = serializer(transaction); const nonHexPrefixedSerializedTx = serializedTx.replace(/^0x/, ""); return await signTransactionWithErrorWrapping(client, nonHexPrefixedSerializedTx, organizationId, signWith); } async function signTypedData(client, data, organizationId, signWith) { const hashToSign = hashTypedData(data); return await signMessageWithErrorWrapping(client, hashToSign, organizationId, signWith); } async function signTransactionWithErrorWrapping(client, unsignedTransaction, organizationId, signWith) { let signedTx; try { signedTx = await signTransactionImpl(client, unsignedTransaction, organizationId, signWith); } catch (error) { // Wrap Turnkey error in Viem-specific error if (error instanceof TurnkeyActivityError$1) { throw new TurnkeyActivityError({ message: error.message, activityId: error.activityId, activityStatus: error.activityStatus, }); } if (error instanceof TurnkeyActivityConsensusNeededError) { throw new TurnkeyConsensusNeededError({ message: error.message, activityId: error.activityId, activityStatus: error.activityStatus, }); } throw new TurnkeyActivityError({ message: `Failed to sign: ${error.message}`, }); } return `0x${signedTx}`; } async function signTransactionImpl(client, unsignedTransaction, organizationId, signWith) { if (client instanceof TurnkeyClient) { const { activity } = await client.signTransaction({ type: "ACTIVITY_TYPE_SIGN_TRANSACTION_V2", organizationId: organizationId, parameters: { signWith, type: "TRANSACTION_TYPE_ETHEREUM", unsignedTransaction: unsignedTransaction, }, timestampMs: String(Date.now()), // millisecond timestamp }); assertActivityCompleted(activity); return assertNonNull(activity?.result?.signTransactionResult?.signedTransaction); } else { const { activity, signedTransaction } = await client.signTransaction({ organizationId, signWith, type: "TRANSACTION_TYPE_ETHEREUM", unsignedTransaction: unsignedTransaction, }); assertActivityCompleted(activity); return assertNonNull(signedTransaction); } } async function signMessageWithErrorWrapping(client, message, organizationId, signWith) { let signedMessage; try { signedMessage = await signMessageImpl(client, message, organizationId, signWith); } catch (error) { // Wrap Turnkey error in Viem-specific error if (error instanceof TurnkeyActivityError$1) { throw new TurnkeyActivityError({ message: error.message, activityId: error.activityId, activityStatus: error.activityStatus, }); } if (error instanceof TurnkeyActivityConsensusNeededError) { throw new TurnkeyConsensusNeededError({ message: error.message, activityId: error.activityId, activityStatus: error.activityStatus, }); } throw new TurnkeyActivityError({ message: `Failed to sign: ${error.message}`, }); } return signedMessage; } async function signMessageImpl(client, message, organizationId, signWith) { let result; if (client instanceof TurnkeyClient) { const { activity } = await client.signRawPayload({ type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2", organizationId: organizationId, parameters: { signWith, payload: message, encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_NO_OP", }, timestampMs: String(Date.now()), // millisecond timestamp }); assertActivityCompleted(activity); result = assertNonNull(activity?.result?.signRawPayloadResult); } else { const { activity, r, s, v } = await client.signRawPayload({ organizationId, signWith, payload: message, encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_NO_OP", }); assertActivityCompleted(activity); result = { r, s, v, }; } return assertNonNull(serializeSignature(result)); } function serializeSignature(sig) { // TODO: update this deprecated method return signatureToHex({ r: `0x${sig.r}`, s: `0x${sig.s}`, v: sig.v === "00" ? 27n : 28n, }); } function isTurnkeyActivityConsensusNeededError(error) { return (typeof error.walk === "function" && error.walk((e) => { return e instanceof TurnkeyConsensusNeededError; })); } function isTurnkeyActivityError(error) { return (typeof error.walk === "function" && error.walk((e) => { return e instanceof TurnkeyActivityError; })); } export { TurnkeyActivityError, TurnkeyConsensusNeededError, createAccount, createAccountWithAddress, createApiKeyAccount, isTurnkeyActivityConsensusNeededError, isTurnkeyActivityError, serializeSignature, signMessage, signTransaction, signTypedData }; //# sourceMappingURL=index.mjs.map