Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecates Celo legacy transaction type (0) #20

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
fca2aa3
docs(CONTRIBUTING): adds instructions for first-time contributors
arthurgousset Apr 16, 2024
8da85b4
chore(Nodejs): bumps required version to 18.14.2
arthurgousset Apr 16, 2024
59ee8f9
chore(dotenv): adds dotenv dependency
arthurgousset Apr 16, 2024
afdaff8
chore(test): adds `.env` example file
arthurgousset Apr 16, 2024
3227dc0
chore(test): adds dotenv and configures file path
arthurgousset Apr 16, 2024
85bd354
chore(test): adds TODOs
arthurgousset Apr 16, 2024
c9dfc81
chore(lib): adds various TODOs
arthurgousset Apr 16, 2024
69c7b83
test(CeloscanProvider): adds TODO note
arthurgousset Apr 16, 2024
ef7d602
chore(transactions): adds TODO
arthurgousset Apr 16, 2024
b6c7c24
fix(CeloscanProvider): update endpoint URL
arthurgousset Apr 17, 2024
0cde16d
nit(test): renaming Forno variable for ease of reference
arthurgousset Apr 17, 2024
46476e7
test(history): fixes tests and extends to 3 networks
arthurgousset Apr 17, 2024
2a7c6e3
test(history): small improvement to description
arthurgousset Apr 17, 2024
f2c2112
chore(history): removes TODO
arthurgousset Apr 17, 2024
1aeeab8
test(history): removes duplicate test
arthurgousset Apr 17, 2024
8faca33
fix(utils): fixes duplicate check in `isCIP64`
arthurgousset Apr 18, 2024
7179c4f
docs(CONTRIBUTING): adds USDC faucet instructions
arthurgousset Apr 18, 2024
1e7c0a5
chore(transactions): deletes legacy celo tx fields
arthurgousset Apr 18, 2024
28b107b
chore(transactions): simplifies and un-nests `getTxType`
arthurgousset Apr 18, 2024
a589019
test(transfers): changes ERC-20 transfer test
arthurgousset Apr 18, 2024
118d109
test(transfers): comments out celo-legacy test
arthurgousset Apr 18, 2024
cb488c6
test(transfers): covers deprecation changes
arthurgousset Apr 18, 2024
500a1b5
test(transfers): adds notes and modifies tests minimally
arthurgousset Apr 18, 2024
c888262
chore(transactions): Removes Celo legacy tx type
arthurgousset Apr 18, 2024
f29e450
chore(CeloWallet): adds TODO note
arthurgousset Apr 18, 2024
263f346
test(transfer): deletes `[celo-legacy]` tests
arthurgousset Apr 22, 2024
391a7f4
chore(CeloProvider): adds `getFeeData(feeCurrency)` and other
arthurgousset Apr 22, 2024
e4d8488
chore(CeloWallet): adds `getFeeData(feeCurrency)` estimation call
arthurgousset Apr 22, 2024
6f71710
chore(transaction): adds `isFeeCurrency` with refactoring TODO
arthurgousset Apr 22, 2024
fd0c0d0
chore(const): deletes duplicate tx type enum
arthurgousset Apr 22, 2024
80568be
test(transfer): adds new and re-organises existing tests
arthurgousset Apr 22, 2024
abe6bc5
test(transfer): moves tests and renames file
arthurgousset Apr 22, 2024
b6f36a8
chore: removes remaining TODOs
arthurgousset Apr 22, 2024
50bd6c3
test(transactions): nit changes expect order
arthurgousset Apr 22, 2024
9d1d30a
docs(CONTRIBUTING): updates nr of CELO and USDC needed
arthurgousset Apr 23, 2024
1f4d587
test(transactions): removes `ethCompatible` test
arthurgousset Apr 23, 2024
4f87e04
chore(common): removes unused `getTransactionByHash`
arthurgousset Apr 23, 2024
ed7dfbe
refactor(CeloWallet): simplifies Promises and uses `isPresent`/`isEmpty`
arthurgousset Apr 23, 2024
c48bd5e
refactor(transactions): simplifies `getTxType`, `isCIP64`, `isEIP1559…
arthurgousset Apr 23, 2024
8c3891d
chore: linting
arthurgousset Apr 23, 2024
4eaa529
nit: typo
arthurgousset Apr 23, 2024
d4ef506
fix(CeloProvider): calculates base fee in fee currency using `eth_gas…
arthurgousset Apr 25, 2024
d504868
chore(CeloProvider): nit removes unnecessary `!`
arthurgousset Apr 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Contributing

Thank you for your interest in improving the celo-ethers-wrapper.

This guide is intended to help you get started with contributing. By following these steps, you will
understand the development process and workflow.

### Cloning the repository

To start contributing to the project, fork it and clone it to your local machine using git:

```sh
$ git clone https://github.com/jmrossy/celo-ethers-wrapper.git
```

Navigate to the project's root directory:

```sh
$ cd celo-ethers-wrapper
```

### Installing Node.js

We use [Node.js](https://nodejs.org/en/) to run the project locally. You need to install the
**Node.js version** specified in [package.json > engines > node](/package.json). To do so, run:

```sh
$ nvm install <specified-version>
$ nvm use <specified-version>
```

### Installing dependencies

Once in the project's root directory, run the following command to install the project's
dependencies:

```sh
$ yarn install
```

After installing the dependencies, the project is ready to be run.

### Running the test suite

1. Create an `.env.test.local` file:

```sh
$ cp tests/.env.test.example tests/.env.test.local
```

2. Generate a brand new wallet (or use an existing development wallet of yours) and paste its
mnemonic phrase into `.env.test.local`:

```txt
MNEMONIC='<PASTE YOUR MNEMONIC PHRASE HERE>'
```

3. Ensure the wallet address has at least 1 CELO and 1 USDC on the Celo Alfajores testnet.

If you need more, you can request:
1. testnet CELO at [faucet.celo.org](https://faucet.celo.org/alfajores), and
2. testnet USDC from
[faucet.circle.com](https://faucet.circle.com/).

4. Now you're ready to run tests with:

```sh
$ yarn test
```

> **INFO** Some tests are run automatically when you open a Pull Request on GitHub.

### Open a Pull Request

✅ Now you're ready to contribute to celo-ethers-wrapper!
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@
"test": "jest ./tests"
},
"engines": {
"node": ">=10"
"node": ">=18.14.2"
},
"devDependencies": {
"@jest/globals": "^29.7.0",
"@types/node": "^20.8.2",
"dotenv": "^16.4.5",
"ethers": "^6.7.1",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
Expand Down
27 changes: 17 additions & 10 deletions src/lib/CeloProvider.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {
FeeData,
JsonRpcProvider,
PerformActionRequest,
TransactionResponse,
TransactionResponseParams,
getBigInt,
resolveProperties,
toBeHex,
} from "ethers";
import { CeloTransactionRequest, parseCeloTransaction } from "./transactions";

Expand All @@ -19,10 +19,7 @@ export default class CeloProvider extends JsonRpcProvider {
// If there are no EIP-1559 properties, it might be non-EIP-1559
if (tx.maxFeePerGas == null && tx.maxPriorityFeePerGas == null) {
const feeData = await this.getFeeData();
if (
feeData.maxFeePerGas == null &&
feeData.maxPriorityFeePerGas == null
) {
if (feeData.maxFeePerGas == null && feeData.maxPriorityFeePerGas == null) {
// Network doesn't know about EIP-1559 (and hence type)
req = Object.assign({}, req, {
transaction: Object.assign({}, tx, { type: undefined }),
Expand All @@ -45,9 +42,7 @@ export default class CeloProvider extends JsonRpcProvider {
* Override to handle alternative gas currencies
* prepareRequest in https://github.com/ethers-io/ethers.js/blob/master/packages/providers/src.ts/json-rpc-provider.ts
*/
getRpcRequest(
req: PerformActionRequest
): null | { method: string; args: Array<any> } {
getRpcRequest(req: PerformActionRequest): null | { method: string; args: Array<any> } {
if (req.method === "getGasPrice") {
// @ts-expect-error
const param = req.feeCurrencyAddress
Expand All @@ -61,8 +56,6 @@ export default class CeloProvider extends JsonRpcProvider {
const extraneous_keys = [
["from", (x: string) => x],
["feeCurrency", (x: string) => x],
["gatewayFeeRecipient", (x: string) => x],
["gatewayFee", toBeHex],
] as const;

const tx = {
Expand Down Expand Up @@ -93,6 +86,20 @@ export default class CeloProvider extends JsonRpcProvider {
);
}

async getFeeData(feeCurrency?: string): Promise<FeeData> {
if (!feeCurrency) {
return super.getFeeData();
}
// On Celo, `eth_gasPrice` returns the base fee for the given currency multiplied 2
// and doesn't include tips. Source: https://github.com/jmrossy/celo-ethers-wrapper/pull/20#discussion_r1579179736
const baseFeePerGasInFeeCurrency = getBigInt(await this.send("eth_gasPrice", [feeCurrency]));
const maxPriorityFeePerGasInFeeCurrency = getBigInt(
await this.send("eth_maxPriorityFeePerGas", [feeCurrency])
);
const maxFeePerGasInFeeCurrency = baseFeePerGasInFeeCurrency + maxPriorityFeePerGasInFeeCurrency;
return new FeeData(null, maxFeePerGasInFeeCurrency, maxPriorityFeePerGasInFeeCurrency);
}

async broadcastTransaction(signedTx: string): Promise<TransactionResponse> {
const { hash } = await resolveProperties({
blockNumber: this.getBlockNumber(),
Expand Down
87 changes: 32 additions & 55 deletions src/lib/CeloWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,8 @@ import {
Wordlist,
} from "ethers";
import CeloProvider from "./CeloProvider";
import { adjustForGasInflation } from "./transaction/utils";
import {
CeloTransaction,
CeloTransactionRequest,
getTxType,
serializeCeloTransaction,
} from "./transactions";
import { adjustForGasInflation, isEmpty } from "./transaction/utils";
import { CeloTransaction, CeloTransactionRequest, serializeCeloTransaction } from "./transactions";

const forwardErrors = [
"INSUFFICIENT_FUNDS",
Expand All @@ -32,33 +27,24 @@ export default class CeloWallet extends Wallet {
* 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: CeloTransactionRequest
): Promise<CeloTransaction> {
async populateTransaction(transaction: CeloTransactionRequest): Promise<CeloTransaction> {
let tx: any = await resolveProperties(transaction);
if (tx.to != null) {
tx.to = Promise.resolve(tx.to);
}

if (tx.from == null) {
if (isEmpty(tx.from)) {
tx.from = this.address;
}

const type = getTxType(tx);
if (!type && tx.gasPrice == null) {
tx.gasPrice = this.getGasPrice();
}

if (tx.nonce == null) {
if (isEmpty(tx.nonce)) {
tx.nonce = await this.provider?.getTransactionCount(tx.from, "pending");
}

tx = await resolveProperties(tx);
if (tx.gasLimit == null) {
tx.gasLimit = this.estimateGas(tx).catch((error) => {
if (isEmpty(tx.gasLimit)) {
try {
tx.gasLimit = await this.estimateGas(tx);
} catch (error: any) {
// 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
assertArgument(
false,
"cannot estimate gas; transaction may fail or may require manual gas limit",
Expand All @@ -68,26 +54,28 @@ export default class CeloWallet extends Wallet {
tx: tx,
}
);
});
}
}

if (tx.chainId == null) {
if (isEmpty(tx.maxPriorityFeePerGas) || isEmpty(tx.maxFeePerGas)) {
const { maxFeePerGas, maxPriorityFeePerGas } = (await (
this.provider as CeloProvider
)?.getFeeData(tx.feeCurrency as string | undefined))!;
tx.maxFeePerGas = maxFeePerGas;
tx.maxPriorityFeePerGas = maxPriorityFeePerGas;
}

if (isEmpty(tx.chainId)) {
tx.chainId = (await this.provider!.getNetwork()).chainId;
} else {
tx.chainId = Promise.all([
Promise.resolve(tx.chainId),
(await this.provider!.getNetwork()).chainId,
]).then((results) => {
if (results[1] !== 0n && results[0] !== results[1]) {
assertArgument(
false,
"chainId address mismatch",
"transaction",
transaction
);
tx.chainId = Promise.all([tx.chainId, (await this.provider!.getNetwork()).chainId]).then(
([txChainId, providerChainId]) => {
if (providerChainId !== 0n && txChainId !== providerChainId) {
assertArgument(false, "chainId address mismatch", "transaction", transaction);
}
return txChainId;
}
return results[0];
});
);
}
return resolveProperties<CeloTransaction>(tx);
}
Expand All @@ -111,19 +99,15 @@ export default class CeloWallet extends Wallet {
delete tx.from;
}

const signature = this.signingKey.sign(
keccak256(serializeCeloTransaction(tx))
);
const signature = this.signingKey.sign(keccak256(serializeCeloTransaction(tx)));
const serialized = serializeCeloTransaction(tx, signature);
return serialized;
}

/**
* Override to serialize transaction using custom serialize method
*/
async sendTransaction(
transaction: CeloTransactionRequest
): Promise<TransactionResponse> {
async sendTransaction(transaction: CeloTransactionRequest): Promise<TransactionResponse> {
const provider = this.provider!;

const pop = await this.populateTransaction(transaction);
Expand All @@ -150,15 +134,8 @@ export default class CeloWallet extends Wallet {
} as PerformActionRequest);
}

static fromMnemonic(
phrase: string,
path?: string,
wordlist?: Wordlist | null
) {
const hdWallet = HDNodeWallet.fromMnemonic(
Mnemonic.fromPhrase(phrase, null, wordlist),
path
);
static fromMnemonic(phrase: string, path?: string, wordlist?: Wordlist | null) {
const hdWallet = HDNodeWallet.fromMnemonic(Mnemonic.fromPhrase(phrase, null, wordlist), path);

return new CeloWallet(hdWallet.privateKey, new CeloProvider());
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/CeloscanProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class CeloscanProvider extends EtherscanProvider {
case "celo":
return "https://api.celoscan.io";
case "alfajores":
return "https://alfajores.celoscan.io";
return "https://api-alfajores.celoscan.io";
case "baklava":
// baklava is currently not supported by celoscan.io, so we use Blockscout
return "https://explorer.celo.org/baklava";
Expand Down
24 changes: 3 additions & 21 deletions src/lib/transaction/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { hexlify, BigNumberish, isBytesLike, toBeHex } from "ethers";
import { GAS_INFLATION_FACTOR } from "../../consts";

function isEmpty(value: string | BigNumberish | undefined | null) {
export function isEmpty(value: string | BigNumberish | undefined | null) {
if (value === undefined || value === null || value === "0" || value === 0n) {
return true;
}
Expand All @@ -11,30 +11,12 @@ function isEmpty(value: string | BigNumberish | undefined | null) {
return toBeHex(value) === "0x0";
}

function isPresent(value: string | BigNumberish | undefined | null) {
export function isPresent(value: string | BigNumberish | undefined | null) {
return !isEmpty(value);
}

export function isEIP1559(tx: any): boolean {
return isPresent(tx.maxFeePerGas) && isPresent(tx.maxPriorityFeePerGas);
}

export function isCIP64(tx: any) {
return (
isEIP1559(tx) &&
isPresent(tx.feeCurrency) &&
!isPresent(tx.gatewayFeeRecipient) &&
!isPresent(tx.gatewayFeeRecipient)
);
}

export function isCIP42(tx: any): boolean {
return (
isEIP1559(tx) &&
(isPresent(tx.feeCurrency) ||
isPresent(tx.gatewayFeeRecipient) ||
isPresent(tx.gatewayFee))
);
return isPresent(tx.feeCurrency);
}

export function concatHex(values: string[]): `0x${string}` {
Expand Down
Loading
Loading