UNPKG

@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
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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