Skip to content

Commit 4651cea

Browse files
authored
chore: upgrade ethers to v6 (#714)
* chore: upgrade ethers to v6 * refactor: avoid bypassing null errors * test: add create-batch test * test: use custom matcher * chore: add step to print docker ps * test: update JSON-RPC URL in create-batch command test * test: fix JSON-RPC URL in create-batch command test * test: update JSON-RPC URL to use HTTP in create-batch command test * test: set SWARM_CLI_NETWORK_ID for create-batch command test * refactor: use SWARM_CLI_NETWORK_ID from environment variable in NETWORK_ID * refactor: replace NETWORK_ID constant with getNetworkId function for dynamic network ID retrieval * fix: handle missing postage stamp log in create batch transaction receipt * refactor: update Contracts to use environment variables for addresses * test: try to debug * test: use correct private key * fix: correct amount in create postage batch log * test: add redeem test * chore: format code * fix: ensure 'to' address starts with '0x' in transaction functions * chore: remove unnecessary docker ps command from CI workflow * fix: handle null gas price and transaction receipt in RPC functions
1 parent dda5046 commit 4651cea

10 files changed

Lines changed: 272 additions & 882 deletions

File tree

package-lock.json

Lines changed: 103 additions & 824 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
"chalk": "^2.4.2",
7070
"cli-progress": "^3.11.2",
7171
"cli-table3": "^0.6.5",
72-
"ethers": "^5.7.2",
72+
"ethers": "^6.16.0",
7373
"furious-commander": "^1.7.1",
7474
"inquirer": "^8.2.5",
7575
"node-fetch": "^2.7.0",

src/command/stake/recover.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export class Recover extends RootCommand implements LeafCommand {
5353
]
5454

5555
const wallet = await createWallet(this.walletSource, this.console)
56-
const signer = await makeReadySigner(wallet.getPrivateKeyString(), this.jsonRpcUrl)
56+
const { signer } = await makeReadySigner(wallet.getPrivateKeyString(), this.jsonRpcUrl)
5757
const contract = new Contract(address, abi, signer)
5858

5959
const isPaused = await contract.paused()

src/command/stamp/buy.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { BatchId, Utils } from '@ethersphere/bee-js'
22
import { Dates, Numbers } from 'cafe-utility'
33
import chalk from 'chalk'
4-
import { BigNumber } from 'ethers'
54
import { LeafCommand, Option } from 'furious-commander'
65
import { exit } from 'process'
76
import { isChainStateReady } from '../../utils/chainsync'
@@ -71,10 +70,10 @@ export class Buy extends StampCommand implements LeafCommand {
7170
}
7271

7372
const chainState = await this.bee.getChainState()
74-
const minimumAmount = BigNumber.from(chainState.currentPrice).mul(17280)
73+
const minimumAmount = BigInt(chainState.currentPrice) * 17280n
7574

76-
if (minimumAmount.gte(BigNumber.from(this.amount))) {
77-
this.console.error('The amount is too low. The minimum amount is', minimumAmount.add(1).toString())
75+
if (minimumAmount >= this.amount) {
76+
this.console.error('The amount is too low. The minimum amount is', (minimumAmount + 1n).toString())
7877

7978
return
8079
}

src/command/utility/create-batch.ts

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Utils } from '@ethersphere/bee-js'
22
import { Numbers, Strings } from 'cafe-utility'
3-
import { Contract, Event, Wallet } from 'ethers'
3+
import { Contract, ContractTransactionReceipt, Wallet } from 'ethers'
44
import { LeafCommand, Option } from 'furious-commander'
55
import { ABI, Contracts } from '../../utils/contracts'
66
import { makeReadySigner } from '../../utils/rpc'
@@ -61,7 +61,7 @@ export class CreateBatch extends RootCommand implements LeafCommand {
6161

6262
const wallet = new Wallet(this.privateKey)
6363
const cost = Utils.getStampCost(this.depth, this.amount)
64-
const signer = await makeReadySigner(wallet.privateKey, this.jsonRpcUrl)
64+
const { signer } = await makeReadySigner(wallet.privateKey, this.jsonRpcUrl)
6565

6666
this.console.log(`Approving spending of ${cost.toDecimalString()} BZZ to ${wallet.address}`)
6767
const tokenProxyContract = new Contract(Contracts.bzz, ABI.tokenProxy, signer)
@@ -76,24 +76,22 @@ export class CreateBatch extends RootCommand implements LeafCommand {
7676

7777
this.console.log(`Creating postage batch for ${wallet.address} with depth ${this.depth} and amount ${this.amount}`)
7878
const postageStampContract = new Contract(Contracts.postageStamp, ABI.postageStamp, signer)
79-
const createBatch = await postageStampContract.createBatch(
80-
signer.address,
81-
this.amount,
82-
this.depth,
83-
16,
84-
`0x${Strings.randomHex(64)}`,
85-
false,
86-
{
87-
gasLimit: 1_000_000,
88-
type: 2,
89-
maxFeePerGas: Numbers.make('3gwei'),
90-
maxPriorityFeePerGas: Numbers.make('2gwei'),
91-
},
92-
)
79+
const createBatchArgs = [signer.address, this.amount, this.depth, 16, `0x${Strings.randomHex(64)}`, false]
80+
const createBatch = await postageStampContract.createBatch(...createBatchArgs, {
81+
gasLimit: 1_000_000,
82+
type: 2,
83+
maxFeePerGas: Numbers.make('3gwei'),
84+
maxPriorityFeePerGas: Numbers.make('2gwei'),
85+
})
9386
this.console.log(`Waiting 3 blocks on create batch tx ${createBatch.hash}`)
94-
const receipt = await createBatch.wait(3)
87+
const receipt = (await createBatch.wait(3)) as ContractTransactionReceipt
9588

96-
const batchId = receipt.events.find((x: Event) => x.address === Contracts.postageStamp).topics[1]
89+
const batchLog = receipt.logs.find(x => x.address === Contracts.postageStamp)
90+
91+
if (!batchLog || batchLog.topics.length < 2) {
92+
throw new Error(`Could not find postage stamp log in receipt. Logs: ${JSON.stringify(receipt.logs)}`)
93+
}
94+
const batchId = batchLog.topics[1]
9795
this.console.log(`Batch created with ID ${batchId}`)
9896
}
9997
}

src/command/utility/redeem.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Dates, System } from 'cafe-utility'
2-
import { BigNumber, providers, Wallet } from 'ethers'
2+
import { JsonRpcProvider, Wallet } from 'ethers'
33
import { Argument, LeafCommand, Option } from 'furious-commander'
4-
import { NETWORK_ID } from '../../utils/contracts'
4+
import { getNetworkId } from '../../utils/contracts'
55
import {
66
estimateNativeTransferTransactionCost,
77
eth_getBalance,
@@ -50,7 +50,7 @@ export class Redeem extends RootCommand implements LeafCommand {
5050
}
5151

5252
this.console.log(`Target wallet address: ${this.target}`)
53-
const provider = new providers.JsonRpcProvider(this.jsonRpcUrl, NETWORK_ID)
53+
const provider = new JsonRpcProvider(this.jsonRpcUrl, getNetworkId())
5454
this.console.log('Creating wallet...')
5555
const wallet = new Wallet(this.wallet, provider)
5656
this.console.log('Fetching xBZZ balance...')
@@ -92,14 +92,14 @@ export class Redeem extends RootCommand implements LeafCommand {
9292
}
9393
}
9494
const { gasPrice, totalCost } = await estimateNativeTransferTransactionCost(this.wallet, this.jsonRpcUrl)
95-
const xDAIValue = BigNumber.from(xDAI)
95+
const xDAIValue = BigInt(xDAI)
9696

97-
if (xDAIValue.gt(totalCost)) {
97+
if (xDAIValue > totalCost) {
9898
this.console.log('Transferring xDAI to Bee wallet...')
9999
await sendNativeTransaction(
100100
this.wallet,
101101
this.target,
102-
xDAIValue.sub(totalCost).toString(),
102+
(xDAIValue - totalCost).toString(),
103103
this.jsonRpcUrl,
104104
gasPrice,
105105
)

src/utils/contracts.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
export const NETWORK_ID = 100
1+
export function getNetworkId(): number {
2+
return process.env.SWARM_CLI_NETWORK_ID ? Number(process.env.SWARM_CLI_NETWORK_ID) : 100
3+
}
24

35
export const Contracts = {
4-
bzz: '0xdBF3Ea6F5beE45c02255B2c26a16F300502F68da',
5-
postageStamp: '0x45a1502382541Cd610CC9068e88727426b696293',
6+
get bzz() {
7+
return process.env.SWARM_CLI_BZZ_ADDRESS ?? '0xdBF3Ea6F5beE45c02255B2c26a16F300502F68da'
8+
},
9+
get postageStamp() {
10+
return process.env.SWARM_CLI_POSTAGE_STAMP_ADDRESS ?? '0x45a1502382541Cd610CC9068e88727426b696293'
11+
},
612
}
713

814
export const ABI = {

src/utils/rpc.ts

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
import { BigNumber as BN, Contract, providers, Wallet } from 'ethers'
2-
import { ABI, Contracts } from './contracts'
1+
import { Contract, JsonRpcProvider, TransactionReceipt, TransactionResponse, Wallet } from 'ethers'
2+
import { ABI, Contracts, getNetworkId } from './contracts'
33

4-
const NETWORK_ID = 100
5-
6-
export async function eth_getBalance(address: string, provider: providers.JsonRpcProvider): Promise<string> {
4+
export async function eth_getBalance(address: string, provider: JsonRpcProvider): Promise<string> {
75
if (!address.startsWith('0x')) {
86
address = `0x${address}`
97
}
@@ -14,7 +12,7 @@ export async function eth_getBalance(address: string, provider: providers.JsonRp
1412

1513
export async function eth_getBalanceERC20(
1614
address: string,
17-
provider: providers.JsonRpcProvider,
15+
provider: JsonRpcProvider,
1816
tokenAddress = Contracts.bzz,
1917
): Promise<string> {
2018
if (!address.startsWith('0x')) {
@@ -27,44 +25,60 @@ export async function eth_getBalanceERC20(
2725
}
2826

2927
interface TransferResponse {
30-
transaction: providers.TransactionResponse
31-
receipt: providers.TransactionReceipt
28+
transaction: TransactionResponse
29+
receipt: TransactionReceipt
3230
}
3331

3432
interface TransferCost {
35-
gasPrice: BN
36-
totalCost: BN
33+
gasPrice: bigint
34+
totalCost: bigint
3735
}
3836

3937
export async function estimateNativeTransferTransactionCost(
4038
privateKey: string,
4139
jsonRpcProvider: string,
4240
): Promise<TransferCost> {
43-
const signer = await makeReadySigner(privateKey, jsonRpcProvider)
44-
const gasLimit = '21000'
45-
const gasPrice = await signer.getGasPrice()
41+
const { provider } = await makeReadySigner(privateKey, jsonRpcProvider)
42+
const gasLimit = 21000n
43+
const { gasPrice } = await provider.getFeeData()
44+
45+
if (gasPrice === null) {
46+
throw new Error('Unable to determine gas price from provider')
47+
}
4648

47-
return { gasPrice, totalCost: gasPrice.mul(gasLimit) }
49+
return { gasPrice, totalCost: gasPrice * gasLimit }
4850
}
4951

5052
export async function sendNativeTransaction(
5153
privateKey: string,
5254
to: string,
5355
value: string,
5456
jsonRpcProvider: string,
55-
externalGasPrice?: BN,
57+
externalGasPrice?: bigint,
5658
): Promise<TransferResponse> {
57-
const signer = await makeReadySigner(privateKey, jsonRpcProvider)
58-
const gasPrice = externalGasPrice ?? (await signer.getGasPrice())
59+
if (!to.startsWith('0x')) {
60+
to = `0x${to}`
61+
}
62+
const { signer, provider } = await makeReadySigner(privateKey, jsonRpcProvider)
63+
const resolvedGasPrice = externalGasPrice ?? (await provider.getFeeData()).gasPrice
64+
65+
if (resolvedGasPrice === null || resolvedGasPrice === undefined) {
66+
throw new Error('Unable to determine gas price from provider')
67+
}
68+
5969
const transaction = await signer.sendTransaction({
6070
to,
61-
value: BN.from(value),
62-
gasPrice,
63-
gasLimit: BN.from(21000),
71+
value: BigInt(value),
72+
gasPrice: resolvedGasPrice,
73+
gasLimit: 21000n,
6474
type: 0,
6575
})
6676
const receipt = await transaction.wait(1)
6777

78+
if (receipt === null) {
79+
throw new Error('Transaction was not included in a block')
80+
}
81+
6882
return { transaction, receipt }
6983
}
7084

@@ -74,19 +88,31 @@ export async function sendBzzTransaction(
7488
value: string,
7589
jsonRpcProvider: string,
7690
): Promise<TransferResponse> {
77-
const signer = await makeReadySigner(privateKey, jsonRpcProvider)
78-
const gasPrice = await signer.getGasPrice()
91+
if (!to.startsWith('0x')) {
92+
to = `0x${to}`
93+
}
94+
const { signer, provider } = await makeReadySigner(privateKey, jsonRpcProvider)
95+
const { gasPrice } = await provider.getFeeData()
96+
97+
if (gasPrice === null) {
98+
throw new Error('Unable to determine gas price from provider')
99+
}
100+
79101
const bzz = new Contract(Contracts.bzz, ABI.bzz, signer)
80102
const transaction = await bzz.transfer(to, value, { gasPrice })
81103
const receipt = await transaction.wait(1)
82104

105+
if (receipt === null) {
106+
throw new Error('Transaction was not included in a block')
107+
}
108+
83109
return { transaction, receipt }
84110
}
85111

86112
export async function makeReadySigner(privateKey: string, jsonRpcProvider: string) {
87-
const provider = new providers.JsonRpcProvider(jsonRpcProvider, NETWORK_ID)
88-
await provider.ready
113+
const provider = new JsonRpcProvider(jsonRpcProvider, getNetworkId())
114+
await provider.getNetwork()
89115
const signer = new Wallet(privateKey, provider)
90116

91-
return signer
117+
return { signer, provider }
92118
}

test/command/create-batch.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { toMatchLinesInOrder } from '../custom-matcher'
2+
import { describeCommand, invokeTestCli } from '../utility'
3+
4+
expect.extend({
5+
toMatchLinesInOrder,
6+
})
7+
8+
describeCommand('Test `utility create-batch` command', ({ consoleMessages }) => {
9+
it('should create batch', async () => {
10+
process.env.SWARM_CLI_NETWORK_ID = '4020'
11+
process.env.SWARM_CLI_BZZ_ADDRESS = '0xe78A0F7E598Cc8b0Bb87894B0F60dD2a88d6a8Ab'
12+
process.env.SWARM_CLI_POSTAGE_STAMP_ADDRESS = '0x254dffcd3277C0b1660F6d42EFbB754edaBAbC2B'
13+
await invokeTestCli([
14+
'utility',
15+
'create-batch',
16+
'--private-key',
17+
'0x566058308ad5fa3888173c741a1fb902c9f1f19559b11fc2738dfc53637ce4e9',
18+
'--depth',
19+
'17',
20+
'--amount',
21+
'10B',
22+
'--json-rpc-url',
23+
'http://localhost:9545',
24+
'--yes',
25+
])
26+
expect(consoleMessages).toMatchLinesInOrder([
27+
['Approving spending of', 'BZZ to'],
28+
['Waiting 3 blocks on approval tx'],
29+
['Creating postage batch for', 'with depth 17 and amount 10000000000'],
30+
['Waiting 3 blocks on create batch tx'],
31+
['Batch created with ID'],
32+
])
33+
})
34+
})

test/command/redeem.spec.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Wallet } from 'ethers'
2+
import { sendBzzTransaction, sendNativeTransaction } from '../../src/utils/rpc'
3+
import { toMatchLinesInOrder } from '../custom-matcher'
4+
import { describeCommand, invokeTestCli } from '../utility'
5+
6+
expect.extend({
7+
toMatchLinesInOrder,
8+
})
9+
10+
const FUNDER_KEY = '0x566058308ad5fa3888173c741a1fb902c9f1f19559b11fc2738dfc53637ce4e9'
11+
const JSON_RPC_URL = 'http://localhost:9545'
12+
const ETH_01 = 100_000_000_000_000_000n.toString()
13+
const BZZ_5 = 50_000_000_000_000_000n.toString()
14+
15+
describeCommand('Test `utility redeem` command', ({ consoleMessages }) => {
16+
it('should redeem funds to target address', async () => {
17+
process.env.SWARM_CLI_NETWORK_ID = '4020'
18+
process.env.SWARM_CLI_BZZ_ADDRESS = '0xe78A0F7E598Cc8b0Bb87894B0F60dD2a88d6a8Ab'
19+
20+
const sourceWallet = Wallet.createRandom()
21+
const targetWallet = Wallet.createRandom()
22+
23+
await sendNativeTransaction(FUNDER_KEY, sourceWallet.address, ETH_01, JSON_RPC_URL)
24+
await sendBzzTransaction(FUNDER_KEY, sourceWallet.address, BZZ_5, JSON_RPC_URL)
25+
26+
await invokeTestCli([
27+
'utility',
28+
'redeem',
29+
sourceWallet.privateKey,
30+
'--json-rpc-url',
31+
JSON_RPC_URL,
32+
'--target',
33+
targetWallet.address,
34+
'--yes',
35+
])
36+
37+
expect(consoleMessages).toMatchLinesInOrder([
38+
['Target wallet address'],
39+
['Creating wallet'],
40+
['xBZZ balance'],
41+
['xDAI balance'],
42+
['Transferring xBZZ'],
43+
['Refreshing xDAI balance'],
44+
['Transferring xDAI'],
45+
['Redeem complete'],
46+
])
47+
}, 60_000)
48+
})

0 commit comments

Comments
 (0)