@f5i23q999d/cow-sdk
Version:
<p align="center"> <img width="400" src="https://github.com/cowprotocol/cow-sdk/raw/main/docs/images/CoW.png" /> </p>
193 lines • 9.09 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateOrderId = exports.getDomain = exports.signOrderCancellations = exports.signOrderCancellation = exports.signOrder = void 0;
const contracts_1 = require("@cowprotocol/contracts");
const contracts_2 = require("../common/consts/contracts.js");
const common_1 = require("../common/index.js");
const order_book_1 = require("../order-book/index.js");
// For error codes, see:
// - https://eth.wiki/json-rpc/json-rpc-error-codes-improvement-proposal
// - https://www.jsonrpc.org/specification#error_object
const METAMASK_SIGNATURE_ERROR_CODE = -32603;
const METHOD_NOT_FOUND_ERROR_CODE = -32601;
// Added the following because of 1Inch wallet who doesn't send the error code
// So we will check the actual error text
const METHOD_NOT_FOUND_ERROR_MSG_REGEX = /Method not found/i;
const V4_ERROR_MSG_REGEX = /eth_signTypedData_v4 does not exist/i;
const V3_ERROR_MSG_REGEX = /eth_signTypedData_v3 does not exist/i;
const RPC_REQUEST_FAILED_REGEX = /RPC request failed/i;
const METAMASK_STRING_CHAINID_REGEX = /provided chainid .* must match the active chainid/i;
const mapSigningSchema = {
[order_book_1.EcdsaSigningScheme.EIP712]: contracts_1.SigningScheme.EIP712,
[order_book_1.EcdsaSigningScheme.ETHSIGN]: contracts_1.SigningScheme.ETHSIGN,
};
function isProviderRpcError(error) {
return error.code !== undefined || error.message !== undefined;
}
async function _signOrder(params) {
const { chainId, signer, order, signingScheme } = params;
const domain = getDomain(chainId);
return (0, contracts_1.signOrder)(domain, order, signer, mapSigningSchema[signingScheme]);
}
async function _signOrderCancellation(params) {
const { chainId, signer, signingScheme, orderUid } = params;
const domain = getDomain(chainId);
return (0, contracts_1.signOrderCancellation)(domain, orderUid, signer, mapSigningSchema[signingScheme]);
}
async function _signOrderCancellations(params) {
const { chainId, signer, signingScheme, orderUids } = params;
const domain = getDomain(chainId);
return (0, contracts_1.signOrderCancellations)(domain, orderUids, signer, mapSigningSchema[signingScheme]);
}
async function _signPayload(payload,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
signFn, signer, signingMethod = 'v4') {
const signingScheme = signingMethod === 'eth_sign' ? order_book_1.EcdsaSigningScheme.ETHSIGN : order_book_1.EcdsaSigningScheme.EIP712;
let signature = null;
let _signer;
try {
switch (signingMethod) {
case 'default':
case 'v3':
_signer = new contracts_1.TypedDataVersionedSigner(signer);
break;
case 'int_v4':
_signer = new contracts_1.IntChainIdTypedDataV4Signer(signer);
break;
default:
_signer = signer;
}
}
catch (e) {
console.error('Wallet not supported:', e);
throw new common_1.CowError('Wallet not supported');
}
try {
signature = (await signFn({ ...payload, signer: _signer, signingScheme })); // Only ECDSA signing supported for now
// eslint-disable-next-line @typescript-eslint/no-explicit-any
}
catch (e) {
if (!isProviderRpcError(e)) {
// Some other error signing. Let it bubble up.
console.error(e);
throw e;
}
const regexErrorCheck = [METHOD_NOT_FOUND_ERROR_MSG_REGEX, RPC_REQUEST_FAILED_REGEX].some((regex) =>
// for example 1Inch error doesn't have e.message so we will check the output of toString()
[e.message, e.toString()].some((msg) => regex.test(msg)));
if (e.code === METHOD_NOT_FOUND_ERROR_CODE || regexErrorCheck) {
// Maybe the wallet returns the proper error code? We can only hope 🤞
// OR it failed with a generic message, there's no error code set, and we also hope it'll work
// with other methods...
switch (signingMethod) {
case 'v4':
return _signPayload(payload, signFn, signer, 'default');
case 'default':
return _signPayload(payload, signFn, signer, 'v3');
case 'v3':
return _signPayload(payload, signFn, signer, 'eth_sign');
default:
throw e;
}
}
else if (METAMASK_STRING_CHAINID_REGEX.test(e.message)) {
// Metamask now enforces chainId to be an integer
return _signPayload(payload, signFn, signer, 'int_v4');
}
else if (e.code === METAMASK_SIGNATURE_ERROR_CODE) {
// We tried to sign order the nice way.
// That works fine for regular MM addresses. Does not work for Hardware wallets, though.
// See https://github.com/MetaMask/metamask-extension/issues/10240#issuecomment-810552020
// So, when that specific error occurs, we know this is a problem with MM + HW.
// Then, we fallback to ETHSIGN.
return _signPayload(payload, signFn, signer, 'eth_sign');
}
else if (V4_ERROR_MSG_REGEX.test(e.message)) {
// Failed with `v4`, and the wallet does not set the proper error code
return _signPayload(payload, signFn, signer, 'v3');
}
else if (V3_ERROR_MSG_REGEX.test(e.message)) {
// Failed with `v3`, and the wallet does not set the proper error code
return _signPayload(payload, signFn, signer, 'eth_sign');
}
else {
// Some other error signing. Let it bubble up.
console.error(e);
throw e;
}
}
const data = signature?.data;
return { signature: data?.toString() || '', signingScheme };
}
/**
* Returns the signature for the specified order with the signing scheme encoded
* into the signature.
* @param {UnsignedOrder} order The order to sign.
* @param {SupportedChainId} chainId The chain Id
* @param {Signer} signer The owner for the order used to sign.
* @return {*} Encoded signature including signing scheme for the order.
*/
async function signOrder(order, chainId, signer) {
return _signPayload({ order, chainId }, _signOrder, signer);
}
exports.signOrder = signOrder;
/**
* Returns the signature for the Order Cancellation with the signing scheme encoded
* into the signature.
* @param {string} orderUid The unique identifier of the order being cancelled.
* @param {SupportedChainId} chainId The chain Id
* @param {Signer} signer The owner for the order used to sign.
* @return {*} Encoded signature including signing scheme for the order.
*/
async function signOrderCancellation(orderUid, chainId, signer) {
return _signPayload({ orderUid, chainId }, _signOrderCancellation, signer);
}
exports.signOrderCancellation = signOrderCancellation;
/**
* Returns the signature for the Order Cancellations with the signing scheme encoded
* into the signature.
*
* @param {string[]} orderUids The unique identifiers of the orders being cancelled.
* @param {SupportedChainId} chainId The CoW Protocol protocol `chainId` context that's being used.
* @param {Signer} signer The owner that had placed the orders used to sign.
* @returns {*} Encoded signature including signing scheme for the order.
*/
async function signOrderCancellations(orderUids, chainId, signer) {
return _signPayload({ orderUids, chainId }, _signOrderCancellations, signer);
}
exports.signOrderCancellations = signOrderCancellations;
/**
* Returns the TypedDataDomain used for signing for the specified chainId.
* @param {SupportedChainId} chainId The chain Id
* @return {*} The TypedDataDomain for the specified chainId.
* @throws {CowError} If the chainId is not supported.
*/
function getDomain(chainId) {
// Get settlement contract address
const settlementContract = contracts_2.COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS[chainId];
if (!settlementContract) {
throw new common_1.CowError('Unsupported network. Settlement contract is not deployed');
}
return (0, contracts_1.domain)(chainId, settlementContract);
}
exports.getDomain = getDomain;
/**
* Generate a deterministic order ID for the specified order.
* @param {SupportedChainId} chainId The chain Id
* @param {Order} order order to sign
* @param {Pick<OrderUidParams, 'owner'>} params order unique identifier parameters.
*/
async function generateOrderId(chainId, order, params) {
const domain = await getDomain(chainId);
const orderDigest = (0, contracts_1.hashOrder)(domain, order);
// Generate the orderId from owner, orderDigest, and max validTo
const orderId = (0, contracts_1.packOrderUidParams)({
...params,
orderDigest,
// Different validTo when signing because EthFlow contract expects it to be max for all orders
validTo: order.validTo,
});
return { orderId, orderDigest };
}
exports.generateOrderId = generateOrderId;
//# sourceMappingURL=utils.js.map