@coinbase/cdp-sdk
Version:
SDK for interacting with the Coinbase Developer Platform Wallet API
145 lines • 6.4 kB
JavaScript
;
/*
* Pure CDP-account-to-x402-signer adapters.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.fromCdpEvmAccount = fromCdpEvmAccount;
exports.resolveNetworkFromChainId = resolveNetworkFromChainId;
exports.fromCdpSmartWallet = fromCdpSmartWallet;
exports.cdpSolanaAccountToSvmSigner = cdpSolanaAccountToSvmSigner;
const kit_1 = require("@solana/kit");
const evm_1 = require("@x402/evm");
const constants_js_1 = require("./constants.js");
/**
* Converts a CDP EVM server account (EOA) into an x402-compatible signer.
*
* @param account - A CDP EVM account from `cdpClient.evm.getOrCreateAccount()`
* @returns A `ClientEvmSigner` compatible with `@x402/evm`'s `registerExactEvmScheme`
*/
function fromCdpEvmAccount(account) {
return (0, evm_1.toClientEvmSigner)(account);
}
/**
* Resolves a CDP SDK network name from an EIP-712 domain chain ID.
*
* @param chainId - The EIP-712 domain chain ID to resolve.
* @throws {Error} If the chain ID is undefined or not supported.
* @returns The CDP SDK network name (e.g. `"base-sepolia"`).
*/
function resolveNetworkFromChainId(chainId) {
if (chainId === undefined) {
throw new Error("Cannot derive CDP network: domain.chainId is missing from the typed data. " +
"EIP-712 domain must include chainId when using a Smart Contract Wallet.");
}
const network = constants_js_1.CHAIN_ID_TO_CDP_NETWORK[chainId];
if (!network) {
throw new Error(`Unsupported chainId ${chainId} for CDP Smart Contract Wallet. ` +
`Supported networks: ${Object.values(constants_js_1.CHAIN_ID_TO_CDP_NETWORK).join(", ")}`);
}
return network;
}
/**
* Converts a CDP Smart Account (EvmSmartAccount) into an x402-compatible signer.
*
* CDP Smart Accounts differ from EOAs in that their `signTypedData` requires a
* `network` parameter. This adapter derives the network automatically from the
* EIP-712 domain's `chainId`.
*
* @param account - A CDP Smart Account from `cdpClient.evm.getOrCreateSmartAccount()`
* @returns A `ClientEvmSigner` compatible with `@x402/evm`'s `registerExactEvmScheme`
*/
function fromCdpSmartWallet(account) {
const signerShape = {
address: account.address,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async signTypedData({ domain, types, primaryType, message }) {
const chainId = domain?.chainId;
const network = resolveNetworkFromChainId(chainId);
return account.signTypedData({ domain, types, primaryType, message, network });
},
};
return (0, evm_1.toClientEvmSigner)(signerShape);
}
/**
* Decodes a Solana short-vector length prefix from the start of a byte array.
*
* @param bytes - Raw transaction bytes to decode.
* @returns Decoded length value and number of bytes consumed.
*/
function decodeShortVecLength(bytes) {
let value = 0;
let shift = 0;
let bytesRead = 0;
while (bytesRead < bytes.length) {
const byte = bytes[bytesRead];
value |= (byte & 0x7f) << shift;
bytesRead += 1;
if ((byte & 0x80) === 0) {
return { value, bytesRead };
}
shift += 7;
}
throw new Error("Invalid Solana transaction wire format: truncated shortvec length.");
}
/**
* Finds the index of a signer in a compiled Solana transaction's signatures map.
*
* @param transaction - Compiled Solana transaction object.
* @param signerAddress - Base58 signer address to locate.
* @returns The zero-based index of the signer in the signatures array.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function resolveSignerIndex(transaction, signerAddress) {
const signatures = transaction?.signatures;
if (signatures && typeof signatures === "object") {
const signerOrder = Object.keys(signatures);
const index = signerOrder.indexOf(signerAddress);
if (index >= 0)
return index;
}
throw new Error(`Unable to locate signer "${signerAddress}" in transaction signatures. ` +
"Expected transaction.signatures to contain the signer address.");
}
/**
* Converts a CDP Solana account into an x402-compatible `TransactionSigner`.
*
* Handles the translation between CDP's base64-encoded transaction strings and
* `@solana/kit`'s compiled transaction objects.
*
* @param account - A CDP Solana account from `cdpClient.solana.getOrCreateAccount()`
* @returns A `TransactionSigner` compatible with `@x402/svm`'s `registerExactSvmScheme`
*/
function cdpSolanaAccountToSvmSigner(account) {
const signerAddress = (0, kit_1.address)(account.address);
const signerAddressKey = signerAddress;
const encoder = (0, kit_1.getTransactionEncoder)();
return {
address: signerAddress,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async signTransactions(transactions) {
const results = [];
for (const transaction of transactions) {
const signerIndex = resolveSignerIndex(transaction, signerAddressKey);
const encodedBytes = encoder.encode(transaction);
const base64Transaction = Buffer.from(encodedBytes).toString("base64");
const { signedTransaction } = await account.signTransaction({
transaction: base64Transaction,
});
const signedBytes = Buffer.from(signedTransaction, "base64");
const { value: signatureCount, bytesRead: signatureSectionOffset } = decodeShortVecLength(signedBytes);
if (signerIndex >= signatureCount) {
throw new Error(`Signer index ${signerIndex} is out of bounds for ${signatureCount} signatures.`);
}
const signatureOffset = signatureSectionOffset + signerIndex * 64;
const signatureEnd = signatureOffset + 64;
if (signatureEnd > signedBytes.length) {
throw new Error("Invalid Solana transaction wire format: signed transaction is missing signature bytes.");
}
const signature = new Uint8Array(signedBytes.slice(signatureOffset, signatureEnd));
results.push({ [signerAddressKey]: signature });
}
return results;
},
};
}
//# sourceMappingURL=account-signers.js.map