@celo-tools/celo-ethers-wrapper
Version:
A minimal wrapper to make Ethers.JS compatible with the Celo network.
428 lines • 35.3 kB
JavaScript
import { accessListify, assertArgument, decodeRlp, encodeRlp, getAddress, getBigInt, getBytes, getNumber, hexlify, isBytesLike, keccak256, recoverAddress, Signature, stripZerosLeft, toBeArray, toBeHex, zeroPadValue, } from "ethers";
import { concatHex, isCIP64, isCIP66 } from "./transaction/utils";
import { EIGHT, EIP155_NUMBER, Y_PARITY_EIP_2098 } from "../consts";
export var TxTypeToPrefix;
(function (TxTypeToPrefix) {
TxTypeToPrefix[TxTypeToPrefix["cip64"] = 123] = "cip64";
TxTypeToPrefix[TxTypeToPrefix["cip66"] = 122] = "cip66";
TxTypeToPrefix[TxTypeToPrefix["eip1559"] = 2] = "eip1559";
})(TxTypeToPrefix || (TxTypeToPrefix = {}));
export const celoAllowedTransactionKeys = {
type: true,
chainId: true,
data: true,
gasLimit: true,
gasPrice: true,
nonce: true,
to: true,
value: true,
feeCurrency: true,
maxFeePerGas: true,
maxPriorityFeePerGas: true,
accessList: true,
maxFeeInFeeCurrency: true,
};
export const celoTransactionFields = {
nonce: { maxLength: 32, numeric: true },
gasPrice: { maxLength: 32, numeric: true },
gasLimit: { maxLength: 32, numeric: true },
feeCurrency: { length: 20 },
to: { length: 20 },
value: { maxLength: 32, numeric: true },
data: {},
maxFeePerGas: { maxLength: 32, numeric: true },
maxPriorityFeePerGas: { maxLength: 32, numeric: true },
maxFeeInFeeCurrency: { maxLength: 32, numeric: true },
};
function formatCeloField(name, value) {
const fieldInfo = celoTransactionFields[name];
const options = {};
if (!value || value === "0x")
return value;
if (fieldInfo.numeric) {
options.hexPad = "left";
value = value ? toBeHex(value, fieldInfo.maxLength) : "0x";
}
else {
value = hexlify(value);
}
let _value = toBeArray(value);
// Fixed-width field
if (fieldInfo.length && _value.length !== fieldInfo.length && _value.length > 0) {
assertArgument(false, "invalid length for " + name, "transaction:" + name, _value);
}
// Variable-width (with a maximum)
if (fieldInfo.maxLength) {
_value = toBeArray(stripZerosLeft(_value));
if (_value.length > fieldInfo.maxLength) {
assertArgument(false, "invalid length for " + name, "transaction:" + name, _value);
}
}
return hexlify(_value);
}
export function getTxType(tx) {
if (tx.gasPrice) {
return "";
}
if (isCIP66(tx)) {
return TxTypeToPrefix.cip66;
}
if (isCIP64(tx)) {
return TxTypeToPrefix.cip64;
}
return TxTypeToPrefix.eip1559;
}
function prepareEncodeTx(tx, signature) {
let raw = [];
switch (tx.type) {
case TxTypeToPrefix.cip66:
// https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip-0066.md
// 0x7a || rlp([chainId, nonce, maxPriorityFeePerGas, maxFeePerGas, gasLimit, to, value, data, accessList, feeCurrency, maxFeeInFeeCurrency, signatureYParity, signatureR, signatureS]).
raw = [
toBeHex(tx.chainId),
toBeHex(tx.nonce),
tx.maxPriorityFeePerGas ? toBeHex(tx.maxPriorityFeePerGas) : "0x",
tx.maxFeePerGas ? toBeHex(tx.maxFeePerGas) : "0x",
tx.gasLimit ? toBeHex(tx.gasLimit) : "0x",
tx.to || "0x",
tx.value ? toBeHex(tx.value) : "0x",
tx.data || "0x",
formatAccessList(tx.accessList || []),
tx.feeCurrency || "0x",
tx.maxFeeInFeeCurrency
? toBeHex(tx.maxFeeInFeeCurrency)
: "0x",
];
break;
case TxTypeToPrefix.cip64:
// https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip-0064.md
// 0x7b || rlp([chainId, nonce, maxPriorityFeePerGas, maxFeePerGas, gasLimit, to, value, data, accessList, feeCurrency, signatureYParity, signatureR, signatureS]).
raw = [
toBeHex(tx.chainId),
toBeHex(tx.nonce),
tx.maxPriorityFeePerGas ? toBeHex(tx.maxPriorityFeePerGas) : "0x",
tx.maxFeePerGas ? toBeHex(tx.maxFeePerGas) : "0x",
tx.gasLimit ? toBeHex(tx.gasLimit) : "0x",
tx.to || "0x",
tx.value ? toBeHex(tx.value) : "0x",
tx.data || "0x",
formatAccessList(tx.accessList || []),
tx.feeCurrency || "0x",
];
break;
case TxTypeToPrefix.eip1559:
// https://eips.ethereum.org/EIPS/eip-1559
// 0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, signature_s]).
raw = [
toBeHex(tx.chainId),
toBeHex(tx.nonce),
tx.maxPriorityFeePerGas ? toBeHex(tx.maxPriorityFeePerGas) : "0x",
tx.maxFeePerGas ? toBeHex(tx.maxFeePerGas) : "0x",
tx.gasLimit ? toBeHex(tx.gasLimit) : "0x",
tx.to || "0x",
tx.value ? toBeHex(tx.value) : "0x",
tx.data || "0x",
formatAccessList(tx.accessList || []),
];
break;
default:
// Type 0 Ethereum legacy transaction:
// rlp([nonce, gasprice, gaslimit, recipient, amount, data, v, r, s])
raw = [
toBeHex(tx.nonce),
tx.gasPrice ? toBeHex(tx.gasPrice) : "0x",
tx.gasLimit ? toBeHex(tx.gasLimit) : "0x",
tx.to || "0x",
tx.value ? toBeHex(tx.value) : "0x",
tx.data || "0x",
];
if (!signature) {
raw.push(...[toBeHex(tx.chainId), "0x", "0x"]);
}
break;
}
if (signature) {
raw.push(...[
// NOTE: Somehow geth cannot parse `0x00` because it's dumb
signature.v ? toBeHex(signature.v) : "0x",
stripZerosLeft(getBytes(signature.r)),
stripZerosLeft(getBytes(signature.s)),
]);
}
return raw;
}
// Almost identical to https://github.com/ethers-io/ethers.js/blob/master/packages/transactions/src.ts/index.ts#L85
// Need to override to use the celo tx prop whitelists above
export function serializeCeloTransaction(transaction, signature) {
Object.keys(transaction).forEach((property) => {
if (!(property in celoAllowedTransactionKeys)) {
assertArgument(false, "unknown property", "serializeCeloTransaction", property);
}
});
const txArgs = {};
Object.entries(transaction).forEach(([fieldName, fieldValue]) => {
if (fieldName in celoTransactionFields) {
// @ts-expect-error
txArgs[fieldName] = formatCeloField(fieldName, fieldValue);
}
else if (fieldName === 'accessList') {
txArgs[fieldName] = fieldValue;
}
});
let chainId = 0;
if (transaction.chainId != null) {
// A chainId was provided; if non-zero we'll use EIP-155
chainId = parseInt(transaction.chainId.toString(16), 16);
if (typeof chainId !== "number") {
assertArgument(false, "invalid transaction.chainId", "transaction", transaction);
}
}
else if (signature && !isBytesLike(signature) && signature.v && getNumber(signature.v) > 28) {
// No chainId provided, but the signature is signing with EIP-155; derive chainId
chainId = Math.floor((getNumber(signature.v) - EIP155_NUMBER) / 2);
}
// We have an EIP-155 transaction (chainId was specified and non-zero)
if (chainId !== 0) {
txArgs["chainId"] = toBeHex(chainId); // @TODO: hexValue?
}
// Requesting an unsigned transation
// @ts-ignore
const type = getTxType(txArgs);
// @ts-expect-error
txArgs.type = type;
if (!signature) {
// @ts-expect-error
const raw = prepareEncodeTx(txArgs);
const encoded = encodeRlp(raw);
return type ? concatHex([toBeHex(type), encoded]) : encoded;
}
// The splitSignature will ensure the transaction has a recoveryParam in the
// case that the signTransaction function only adds a v.
const sig = Signature.from(signature);
let v;
if (txArgs.type) {
// cip64, eip-1559
v = sig.v - Y_PARITY_EIP_2098;
}
else {
// celo-legacy
v = Y_PARITY_EIP_2098 + sig.yParity;
if (chainId !== 0) {
v += chainId * 2 + EIGHT;
// If an EIP-155 v (directly or indirectly; maybe _vs) was provided, check it!
if (sig.v > Y_PARITY_EIP_2098 + 1 && sig.v !== v) {
assertArgument(false, "transaction.chainId/signature.v mismatch", "signature", signature);
}
}
else if (sig.v !== v) {
assertArgument(false, "transaction.chainId/signature.v mismatch", "signature", signature);
}
}
// @ts-expect-error
const raw = prepareEncodeTx(txArgs, { ...sig.toJSON(), v });
const encoded = encodeRlp(raw);
return type ? concatHex([toBeHex(type), encoded]) : encoded;
}
// Based on https://github.com/ethers-io/ethers.js/blob/0234cfbbef76b7f7a53efe4c434cc6d8892bf404/packages/transactions/src.ts/index.ts#L165
// Need to override to use the celo tx prop whitelists above
export function parseCeloTransaction(rawTransaction) {
const [type, transaction] = splitTypeAndRawTx(rawTransaction);
let tx;
switch (type) {
case TxTypeToPrefix.cip66:
tx = {
type: TxTypeToPrefix.cip66,
chainId: handleNumber(transaction[0]),
nonce: handleNumber(transaction[1]),
maxPriorityFeePerGas: handleBigInt(transaction[2]),
maxFeePerGas: handleBigInt(transaction[3]),
gasLimit: handleBigInt(transaction[4]),
to: handleAddress(transaction[5]),
value: handleBigInt(transaction[6]),
data: transaction[7],
accessList: handleAccessList(transaction[8]),
feeCurrency: handleAddress(transaction[9]),
maxFeeInFeeCurrency: handleBigInt(transaction[10]),
};
break;
case TxTypeToPrefix.cip64:
tx = {
type: TxTypeToPrefix.cip64,
chainId: handleNumber(transaction[0]),
nonce: handleNumber(transaction[1]),
maxPriorityFeePerGas: handleBigInt(transaction[2]),
maxFeePerGas: handleBigInt(transaction[3]),
gasLimit: handleBigInt(transaction[4]),
to: handleAddress(transaction[5]),
value: handleBigInt(transaction[6]),
data: transaction[7],
accessList: handleAccessList(transaction[8]),
feeCurrency: handleAddress(transaction[9]),
};
break;
case TxTypeToPrefix.eip1559:
// untested
tx = {
type: TxTypeToPrefix.eip1559,
chainId: handleNumber(transaction[0]),
nonce: handleNumber(transaction[1]),
maxPriorityFeePerGas: handleBigInt(transaction[2]),
maxFeePerGas: handleBigInt(transaction[3]),
gasLimit: handleBigInt(transaction[4]),
to: handleAddress(transaction[5]),
value: handleBigInt(transaction[6]),
data: transaction[7],
accessList: handleAccessList(transaction[8]),
};
break;
default:
/**
* Type 0 Ethereum legacy transaction:
* RLP([nonce, gasprice, gaslimit, recipient, amount, data, chaindId, 0, 0])
*/
tx = {
nonce: handleNumber(transaction[0]),
gasPrice: handleBigInt(transaction[1]),
gasLimit: handleBigInt(transaction[2]),
to: handleAddress(transaction[3]),
value: handleBigInt(transaction[4]),
data: transaction[5],
chainId: handleBigInt(transaction[6]),
};
break;
}
// Legacy unsigned transaction
if (!isSigned(type, transaction)) {
return tx;
}
let v;
try {
v = handleNumber(transaction.at(-3));
}
catch (error) {
console.log(error);
return tx;
}
const r = zeroPadValue(transaction.at(-2), 32);
const s = zeroPadValue(transaction.at(-1), 32);
// EIP-155 unsigned transaction
if (handleBigInt(r) === 0n && handleBigInt(s) === 0n) {
if (!type) {
tx.chainId = v;
v = 0;
}
}
// Signed Transaction
else {
let recoveryParam = v;
if (!type) {
// celo-legacy
tx.chainId = Math.max(0, Math.floor((v - EIP155_NUMBER) / 2));
recoveryParam = v - Y_PARITY_EIP_2098;
recoveryParam -= tx.chainId * 2 + EIGHT;
}
// NOTE: Serialization needs to happen here because chainId may not populated before
const serialized = serializeCeloTransaction(tx);
const digest = keccak256(serialized);
try {
tx.from = recoverAddress(digest, {
r,
s,
yParity: recoveryParam,
});
}
catch (error) {
console.log(error);
}
tx.hash = keccak256(rawTransaction);
}
return tx;
}
function handleAddress(value) {
if (value === "0x") {
return undefined;
}
try {
return getAddress(value);
}
catch (error) {
return value;
}
}
function handleNumber(value) {
if (value === "0x") {
return 0;
}
return getNumber(value);
}
function handleBigInt(value) {
if (value === "0x") {
return 0n;
}
return getBigInt(value);
}
function formatAccessList(value) {
return accessListify(value).map((set) => [set.address, set.storageKeys]);
}
/*
@param value [
[
'0x0000000000000000000000000000000000000000',
[
'0x0000000000000000000000000000000000000000000000000000000000000001',
'0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe'
]
]
]
*/
function handleAccessList(value) {
if (value === "0x") {
return accessListify([]);
}
return accessListify(value);
}
const baseTxLengths = {
[TxTypeToPrefix.cip64]: { unsigned: 10, signed: 13 },
[TxTypeToPrefix.cip66]: { unsigned: 11, signed: 14 },
[TxTypeToPrefix.eip1559]: { unsigned: 9, signed: 12 },
/**
* Unsigned: RLP([nonce, gasprice, gaslimit, recipient, amount, data, chaindId, 0, 0])
* Signed: RLP([nonce, gasprice, gaslimit, recipient, amount, data, v, r, s])
* Source: https://github.com/celo-org/txtypes?tab=readme-ov-file#legacy-transactions
*/
"ethereum-legacy": { unsigned: 9, signed: 9 },
};
function isSigned(type, transaction) {
if (type) {
const { signed } = baseTxLengths[type];
return transaction.length === signed;
}
const r = transaction.at(-2) || "0x";
const s = transaction.at(-1) || "0x";
return r !== "0x" && s !== "0x";
}
function isCorrectLength(type, transaction) {
const { unsigned } = baseTxLengths[type || "ethereum-legacy"];
return transaction.length === unsigned || isSigned(type, transaction);
}
function splitTypeAndRawTx(rawTransaction) {
let rawStr = rawTransaction.toString();
let type;
for (const _type of [TxTypeToPrefix.cip64, TxTypeToPrefix.cip66, TxTypeToPrefix.eip1559]) {
const prefix = toBeHex(_type);
if (rawStr.startsWith(prefix)) {
rawStr = `0x${rawStr.slice(prefix.length)}`;
type = _type;
break;
}
}
const transaction = decodeRlp(rawStr);
if (!isCorrectLength(type, transaction)) {
assertArgument(false, "invalid raw transaction", "{type, rawTransaction}", {
type: type,
rawTransaction,
});
}
return [type, transaction];
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhbnNhY3Rpb25zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2xpYi90cmFuc2FjdGlvbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUVMLGFBQWEsRUFFYixjQUFjLEVBRWQsU0FBUyxFQUNULFNBQVMsRUFDVCxVQUFVLEVBQ1YsU0FBUyxFQUNULFFBQVEsRUFDUixTQUFTLEVBQ1QsT0FBTyxFQUNQLFdBQVcsRUFDWCxTQUFTLEVBQ1QsY0FBYyxFQUVkLFNBQVMsRUFFVCxjQUFjLEVBQ2QsU0FBUyxFQUNULE9BQU8sRUFHUCxZQUFZLEdBQ2IsTUFBTSxRQUFRLENBQUM7QUFDaEIsT0FBTyxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDbEUsT0FBTyxFQUFFLEtBQUssRUFBRSxhQUFhLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxXQUFXLENBQUM7QUE0QnBFLE1BQU0sQ0FBTixJQUFZLGNBSVg7QUFKRCxXQUFZLGNBQWM7SUFDeEIsdURBQVksQ0FBQTtJQUNaLHVEQUFZLENBQUE7SUFDWix5REFBYyxDQUFBO0FBQ2hCLENBQUMsRUFKVyxjQUFjLEtBQWQsY0FBYyxRQUl6QjtBQVFELE1BQU0sQ0FBQyxNQUFNLDBCQUEwQixHQUFHO0lBQ3hDLElBQUksRUFBRSxJQUFJO0lBQ1YsT0FBTyxFQUFFLElBQUk7SUFDYixJQUFJLEVBQUUsSUFBSTtJQUNWLFFBQVEsRUFBRSxJQUFJO0lBQ2QsUUFBUSxFQUFFLElBQUk7SUFDZCxLQUFLLEVBQUUsSUFBSTtJQUNYLEVBQUUsRUFBRSxJQUFJO0lBQ1IsS0FBSyxFQUFFLElBQUk7SUFDWCxXQUFXLEVBQUUsSUFBSTtJQUNqQixZQUFZLEVBQUUsSUFBSTtJQUNsQixvQkFBb0IsRUFBRSxJQUFJO0lBQzFCLFVBQVUsRUFBRSxJQUFJO0lBQ2hCLG1CQUFtQixFQUFFLElBQUk7Q0FDakIsQ0FBQztBQXFCWCxNQUFN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