@infibridge/celo-sdk-base
Version:
EthersJS wrapper for Celo Blockchain. Based on https://github.com/celo-tools/celo-ethers-wrapper with small modifications.
179 lines • 15.4 kB
JavaScript
import { BigNumber, constants, utils, } from 'ethers';
const logger = new utils.Logger('CeloTransactionsWrapper');
export const celoTransactionFields = [
{ name: 'nonce', maxLength: 32, numeric: true },
{ name: 'gasPrice', maxLength: 32, numeric: true },
{ name: 'gasLimit', maxLength: 32, numeric: true },
{ name: 'feeCurrency', length: 20 },
{ name: 'gatewayFeeRecipient', length: 20 },
{ name: 'gatewayFee', maxLength: 32, numeric: true },
{ name: 'to', length: 20 },
{ name: 'value', maxLength: 32, numeric: true },
{ name: 'data' },
];
export const celoAllowedTransactionKeys = {
chainId: true,
data: true,
gasLimit: true,
gasPrice: true,
nonce: true,
to: true,
value: true,
feeCurrency: true,
gatewayFeeRecipient: true,
gatewayFee: true,
};
// 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) {
utils.checkProperties(transaction, celoAllowedTransactionKeys);
const raw = [];
celoTransactionFields.forEach(function (fieldInfo) {
let value = transaction[fieldInfo.name] || [];
const options = {};
if (fieldInfo.numeric) {
options.hexPad = 'left';
}
value = utils.arrayify(utils.hexlify(value, options));
// Fixed-width field
if (fieldInfo.length &&
value.length !== fieldInfo.length &&
value.length > 0) {
logger.throwArgumentError('invalid length for ' + fieldInfo.name, 'transaction:' + fieldInfo.name, value);
}
// Variable-width (with a maximum)
if (fieldInfo.maxLength) {
value = utils.stripZeros(value);
if (value.length > fieldInfo.maxLength) {
logger.throwArgumentError('invalid length for ' + fieldInfo.name, 'transaction:' + fieldInfo.name, value);
}
}
raw.push(utils.hexlify(value));
});
let chainId = 0;
if (transaction.chainId != null) {
// A chainId was provided; if non-zero we'll use EIP-155
chainId = transaction.chainId;
if (typeof chainId !== 'number') {
logger.throwArgumentError('invalid transaction.chainId', 'transaction', transaction);
}
}
else if (signature &&
!utils.isBytesLike(signature) &&
signature.v &&
signature.v > 28) {
// No chainId provided, but the signature is signing with EIP-155; derive chainId
chainId = Math.floor((signature.v - 35) / 2);
}
// We have an EIP-155 transaction (chainId was specified and non-zero)
if (chainId !== 0) {
raw.push(utils.hexlify(chainId)); // @TODO: hexValue?
raw.push('0x');
raw.push('0x');
}
// Requesting an unsigned transation
if (!signature) {
return utils.RLP.encode(raw);
}
// The splitSignature will ensure the transaction has a recoveryParam in the
// case that the signTransaction function only adds a v.
const sig = utils.splitSignature(signature);
// We pushed a chainId and null r, s on for hashing only; remove those
let v = 27 + sig.recoveryParam;
if (chainId !== 0) {
raw.pop();
raw.pop();
raw.pop();
v += chainId * 2 + 8;
// If an EIP-155 v (directly or indirectly; maybe _vs) was provided, check it!
if (sig.v > 28 && sig.v !== v) {
logger.throwArgumentError('transaction.chainId/signature.v mismatch', 'signature', signature);
}
}
else if (sig.v !== v) {
logger.throwArgumentError('transaction.chainId/signature.v mismatch', 'signature', signature);
}
raw.push(utils.hexlify(v));
raw.push(utils.stripZeros(utils.arrayify(sig.r)));
raw.push(utils.stripZeros(utils.arrayify(sig.s)));
return utils.RLP.encode(raw);
}
// Almost identical to https://github.com/ethers-io/ethers.js/blob/master/packages/transactions/src.ts/index.ts#L165
// Need to override to use the celo tx prop whitelists above
export function parseCeloTransaction(rawTransaction) {
const transaction = utils.RLP.decode(rawTransaction);
if (transaction.length !== 12 && transaction.length !== 9) {
logger.throwArgumentError('invalid raw transaction', 'rawTransaction', rawTransaction);
}
const tx = {
nonce: handleNumber(transaction[0]).toNumber(),
gasPrice: handleNumber(transaction[1]),
gasLimit: handleNumber(transaction[2]),
feeCurrency: handleAddress(transaction[3]),
gatewayFeeRecipient: handleAddress(transaction[4]),
gatewayFee: handleNumber(transaction[5]),
to: handleAddress(transaction[6]),
value: handleNumber(transaction[7]),
data: transaction[8],
chainId: 0,
};
// Legacy unsigned transaction
if (transaction.length === 9) {
return tx;
}
try {
tx.v = BigNumber.from(transaction[9]).toNumber();
}
catch (error) {
console.log(error);
return tx;
}
tx.r = utils.hexZeroPad(transaction[10], 32);
tx.s = utils.hexZeroPad(transaction[11], 32);
if (BigNumber.from(tx.r).isZero() && BigNumber.from(tx.s).isZero()) {
// EIP-155 unsigned transaction
tx.chainId = tx.v;
tx.v = 0;
}
else {
// Signed Tranasaction
tx.chainId = Math.floor((tx.v - 35) / 2);
if (tx.chainId < 0) {
tx.chainId = 0;
}
let recoveryParam = tx.v - 27;
const raw = transaction.slice(0, 6);
if (tx.chainId !== 0) {
raw.push(utils.hexlify(tx.chainId));
raw.push('0x');
raw.push('0x');
recoveryParam -= tx.chainId * 2 + 8;
}
const digest = utils.keccak256(utils.RLP.encode(raw));
try {
tx.from = utils.recoverAddress(digest, {
r: utils.hexlify(tx.r),
s: utils.hexlify(tx.s),
recoveryParam: recoveryParam,
});
}
catch (error) {
console.log(error);
}
tx.hash = utils.keccak256(rawTransaction);
}
return tx;
}
function handleAddress(value) {
if (value === '0x') {
return undefined;
}
return utils.getAddress(value);
}
function handleNumber(value) {
if (value === '0x') {
return constants.Zero;
}
return BigNumber.from(value);
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ2Vsb1RyYW5zYWN0aW9uc1dyYXBwZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvQ2Vsb1RyYW5zYWN0aW9uc1dyYXBwZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNILFNBQVMsRUFHVCxTQUFTLEVBR1QsS0FBSyxHQUNSLE1BQU0sUUFBUSxDQUFBO0FBY2YsTUFBTSxNQUFNLEdBQUcsSUFBSSxLQUFLLENBQUMsTUFBTSxDQUFDLHlCQUF5QixDQUFDLENBQUE7QUFjMUQsTUFBTSxDQUFDLE1BQU0scUJBQXFCLEdBQUc7SUFDakMsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxFQUFFLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRTtJQUMvQyxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsU0FBUyxFQUFFLEVBQUUsRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFO0lBQ2xELEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUU7SUFDbEQsRUFBRSxJQUFJLEVBQUUsYUFBYSxFQUFFLE1BQU0sRUFBRSxFQUFFLEVBQUU7SUFDbkMsRUFBRSxJQUFJLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxFQUFFLEVBQUUsRUFBRTtJQUMzQyxFQUFFLElBQUksRUFBRSxZQUFZLEVBQUUsU0FBUyxFQUFFLEVBQUUsRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFO0lBQ3BELEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsRUFBRSxFQUFFO0lBQzFCLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUU7SUFDL0MsRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFO0NBQ25CLENBQUE7QUFFRCxNQUFNLENBQUMsTUFBTSwwQkFBMEIsR0FBK0I7SUFDbEUsT0FBTyxFQUFFLElBQUk7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