@celo-tools/celo-ethers-wrapper
Version:
A minimal wrapper to make Ethers.JS compatible with the Celo network.
168 lines (167 loc) • 13.6 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const ethers_1 = require("ethers");
const CeloProvider_1 = __importDefault(require("./CeloProvider"));
const utils_1 = require("./transaction/utils");
const transactions_1 = require("./transactions");
const consts_1 = require("../consts");
const CoreContract_1 = require("./CoreContract");
const forwardErrors = [
"INSUFFICIENT_FUNDS",
"NONCE_EXPIRED",
"REPLACEMENT_UNDERPRICED",
];
class CeloWallet extends ethers_1.Wallet {
async isCel2() {
const code = await this.provider?.getCode(consts_1.L2_PROXY_ADMIN_ADDRESS);
if (typeof code === "string") {
return code != "0x" && code.length > 2;
}
return false;
}
/**
* Override to skip checkTransaction step which rejects Celo tx properties
* https://github.com/ethers-io/ethers.js/blob/master/packages/abstract-signer/src.ts/index.ts
*/
async populateTransaction(transaction) {
let tx = await (0, ethers_1.resolveProperties)(transaction);
if ((0, utils_1.isEmpty)(tx.from)) {
tx.from = this.address;
}
if ((0, utils_1.isEmpty)(tx.nonce)) {
tx.nonce = await this.provider?.getTransactionCount(tx.from, "pending");
}
if ((0, utils_1.isEmpty)(tx.gasLimit)) {
try {
tx.gasLimit = await this.estimateGas(tx);
}
catch (error) {
// If there is an error code it's an expected error
if (forwardErrors.indexOf(error.code) >= 0) {
throw error;
}
// If there is no error code it's an unexpected error
(0, ethers_1.assertArgument)(false, "cannot estimate gas; transaction may fail or may require manual gas limit", "transaction", {
error: error,
tx: tx,
});
}
}
await this.populateFees(tx);
if ((0, utils_1.isEmpty)(tx.chainId)) {
tx.chainId = (await this.provider.getNetwork()).chainId;
}
else {
tx.chainId = Promise.all([
tx.chainId,
(await this.provider.getNetwork()).chainId,
]).then(([txChainId, providerChainId]) => {
if (providerChainId !== 0n && txChainId !== providerChainId) {
(0, ethers_1.assertArgument)(false, "chainId address mismatch", "transaction", transaction);
}
return txChainId;
});
}
return (0, ethers_1.resolveProperties)(tx);
}
// sets feedata for the transaction.
//
async populateFees(tx) {
const noFeeCurrency = !tx.feeCurrency;
const useCIP66ForEasyFeeTransactions = false; // cip66 is not yet implemented in nodes
// CIP 66 transactions are denominated in CELO not the fee token
const feesAreInCELO = noFeeCurrency || useCIP66ForEasyFeeTransactions;
if ((0, utils_1.isEmpty)(tx.maxPriorityFeePerGas) || (0, utils_1.isEmpty)(tx.maxFeePerGas)) {
const { maxFeePerGas, maxPriorityFeePerGas } = (await this.provider?.getFeeData(tx.feeCurrency, feesAreInCELO));
tx.maxFeePerGas = maxFeePerGas;
tx.maxPriorityFeePerGas = maxPriorityFeePerGas;
if (useCIP66ForEasyFeeTransactions && (0, utils_1.isEmpty)(tx.maxFeeInFeeCurrency)) {
const gasLimit = BigInt(tx.gasLimit);
const maxFeeInFeeCurrency = await this.estimateMaxFeeInFeeToken({
feeCurrency: tx.feeCurrency,
gasLimit,
maxFeePerGas: maxFeePerGas,
});
tx.maxFeeInFeeCurrency = maxFeeInFeeCurrency;
}
}
return tx;
}
/**
* Override to serialize transaction using custom serialize method
* https://github.com/ethers-io/ethers.js/blob/master/packages/wallet/src.ts/index.ts
*/
async signTransaction(transaction) {
const tx = await this.populateTransaction(transaction);
if (tx.from != null) {
if ((0, ethers_1.getAddress)(tx.from) !== this.address) {
(0, ethers_1.assertArgument)(false, "transaction from address mismatch", "transaction.from", transaction.from);
}
delete tx.from;
}
const signature = this.signingKey.sign((0, ethers_1.keccak256)((0, transactions_1.serializeCeloTransaction)(tx)));
const serialized = (0, transactions_1.serializeCeloTransaction)(tx, signature);
return serialized;
}
/**
* Override to serialize transaction using custom serialize method
*/
async sendTransaction(transaction) {
const provider = this.provider;
const pop = await this.populateTransaction(transaction);
delete pop.from;
return await provider.broadcastTransaction(await this.signTransaction(pop));
}
/**
* Override to skip checkTransaction step which rejects Celo tx properties
* https://github.com/ethers-io/ethers.js/blob/master/packages/abstract-signer/src.ts/index.ts
*/
async estimateGas(transaction) {
return this.provider.estimateGas(transaction).then(utils_1.adjustForGasInflation);
}
/**
* @remarks For cip 66 transactions it is necessary to provide the absolute limit one is willing to pay denominated in the token.
* In contrast with earlier tx types for fee currencies (celo legacy, cip42, cip 64).
* Calulating Estimation requires the gas, maxfeePerGas and the conversion rate from CELO to feeToken
* https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip-0066.md
*
* @dev cip66 has yet to be implemented on celo nodes. Dont add maxFeeInFeeCurrency to your transaction. use cip64
*/
async estimateMaxFeeInFeeToken({ gasLimit, maxFeePerGas, feeCurrency, }) {
const maxGasFeesInCELO = gasLimit * maxFeePerGas;
const [numerator, denominator] = await (0, CoreContract_1.getConversionRateFromCeloToToken)(feeCurrency, this);
const feeDenominatedInToken = (0, utils_1.convertFromCeloToToken)({
amountInCelo: maxGasFeesInCELO,
ratioTOKEN: numerator,
ratioCELO: denominator,
});
return feeDenominatedInToken;
}
/**
* Override to support alternative gas currencies
* @dev (for cip66 txn you want gasPrice in CELO so dont pass in the feeToken)
* https://github.com/celo-tools/ethers.js/blob/master/packages/abstract-signer/src.ts/index.ts
*/
async getGasPrice(feeCurrencyAddress) {
return await this.provider._perform({
method: "getGasPrice",
feeCurrencyAddress,
});
}
static fromMnemonic(phrase, path, wordlist) {
const hdWallet = ethers_1.HDNodeWallet.fromMnemonic(ethers_1.Mnemonic.fromPhrase(phrase, null, wordlist), path);
return new CeloWallet(hdWallet.privateKey, new CeloProvider_1.default());
}
/**
* Override just for type fix
* https://github.com/ethers-io/ethers.js/blob/master/packages/wallet/src.ts/index.ts
*/
connect(provider) {
return new CeloWallet(this.signingKey, provider);
}
}
exports.default = CeloWallet;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ2Vsb1dhbGxldC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvQ2Vsb1dhbGxldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLG1DQWFnQjtBQUNoQixrRUFBMEM7QUFDMUMsK0NBSTZCO0FBQzdCLGlEQUl3QjtBQUN4QixzQ0FBbUQ7QUFDbkQsaURBQWtFO0FBRWxFLE1BQU0sYUFBYSxHQUFHO0lBQ3BCLG9CQUFvQjtJQUNwQixlQUFlO0lBQ2YseUJBQXlCO0NBQ1gsQ0FBQztBQUVqQixNQUFxQixVQUFXLFNBQVEsZUFBTTtJQUM1QyxLQUFLLENBQUMsTUFBTTtRQUNWLE1BQU0sSUFBSSxHQUFHLE1BQU0sSUFBSSxDQUFDLFFBQVEsRUFBRSxPQUFPLENBQUMsK0JBQXNCLENBQUMsQ0FBQztRQUNsRSxJQUFJLE9BQU8sSUFBSSxLQUFLLFFBQVEsRUFBRTtZQUM1QixPQUFPLElBQUksSUFBSSxJQUFJLElBQUksSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7U0FDeEM7UUFDRCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRDs7O09BR0c7SUFDSCxLQUFLLENBQUMsbUJBQW1CLENBQ3ZCLFdBQW1DO1FBRW5DLElBQUksRUFBRSxHQUFRLE1BQU0sSUFBQSwwQkFBaUIsRUFBQyxXQUFXLENBQUMsQ0FBQztRQUVuRCxJQUFJLElBQUEsZUFBTyxFQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRTtZQUNwQixFQUFFLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUM7U0FDeEI7UUFDRCxJQUFJLElBQUEsZUFBTyxFQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsRUFBRTtZQUNyQixFQUFFLENBQUMsS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLFFBQVEsRUFBRSxtQkFBbUIsQ0FBQyxFQUFFLENBQUMsSUFBSSxFQUFFLFNBQVMsQ0FBQyxDQUFDO1NBQ3pFO1FBQ0QsSUFBSSxJQUFBLGVB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