Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [3.13.0] - 2026-10-22

- Replace the unmaintained web3-onboard wallet library with RainbowKit on wagmi 2 and viem.
- Connect through MetaMask, WalletConnect (which also reaches Ledger Live), Base (Coinbase Wallet) and Safe, which connects automatically when the explorer is opened as a Safe App.
- Move the contract and services layer from ethers v5 to viem, with ABIs and deployment addresses generated by `@wagmi/cli` (`pnpm codegen`) from the `@cartesi` contract packages.
- Resolve ENS avatars on the server through viem's universal resolver.
- Move `ethers` and the `@cartesi` contract packages to devDependencies (ABI generation only) and remove `@ethersproject/*`, `eth-chains` and `eth-rpc-errors`; the `elliptic` advisory no longer reaches the production dependency tree.
- Wait for Safe App transactions through the Safe provider, so the confirmation banner resolves once the multisig executes the transaction.
- Replace `react-gtm-module` with the Google Tag Manager component from `@next/third-parties`.

## [3.12.8] - 2026-09-23

Expand Down Expand Up @@ -491,7 +495,8 @@ Staking Pools

- First release

[unreleased]: https://github.com/cartesi/explorer/compare/v3.12.8...HEAD
[unreleased]: https://github.com/cartesi/explorer/compare/v3.13.0...HEAD
[3.13.0]: https://github.com/cartesi/explorer/compare/v3.13.0...v3.12.8
[3.12.8]: https://github.com/cartesi/explorer/compare/v3.12.8...v3.12.7
[3.12.7]: https://github.com/cartesi/explorer/compare/v3.12.7...v3.12.6
[3.12.6]: https://github.com/cartesi/explorer/compare/v3.12.6...v3.12.5
Expand Down
2 changes: 1 addition & 1 deletion __tests__/components/pools/steps/CommissionModel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ describe('CommissionModel step component', () => {
).toBeInTheDocument();
expect(
screen.getByText(
'Pool 0xE656584736b1EFC14b4b6c785AA9C23BAc8f41AA created! moving to the next step...',
'Pool 0xE656584736b1EFC14b4b6c785AA9C23BAc8f41AA created! Moving to the next step...',
),
).toBeInTheDocument();
});
Expand Down
10 changes: 7 additions & 3 deletions __tests__/components/pools/steps/ENS.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ describe('Pool ENS step', () => {
expect(screen.getByText('1')).toBeInTheDocument();
expect(screen.getByText('Pool ENS')).toBeInTheDocument();
expect(
screen.getByText('Registering a ENS domain and setting it up.'),
screen.getByText(
'Registering an ENS domain and setting it up.',
),
).toBeInTheDocument();

expect(
Expand All @@ -147,7 +149,9 @@ describe('Pool ENS step', () => {
expect(screen.getByText('1')).toBeInTheDocument();
expect(screen.getByText('Pool ENS')).toBeInTheDocument();
expect(
screen.getByText('Registering a ENS domain and setting it up.'),
screen.getByText(
'Registering an ENS domain and setting it up.',
),
).toBeInTheDocument();

expect(
Expand Down Expand Up @@ -360,7 +364,7 @@ describe('Pool ENS step', () => {
).toBeInTheDocument();

expect(
await screen.findByText('Pool ENS updated with success!'),
await screen.findByText('Pool ENS updated successfully!'),
).toBeInTheDocument();

// Checking even though the step-state router is controlled the router is still called
Expand Down
2 changes: 1 addition & 1 deletion __tests__/components/pools/steps/HireNode.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ describe('HireNode Step', () => {
expect(
await findByText(
alert,
'Node hired! moving to the next step...',
'Node hired! Moving to the next step...',
),
).toBeInTheDocument();
});
Expand Down
80 changes: 80 additions & 0 deletions __tests__/services/transaction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,90 @@ describe('Transaction service', () => {
expect(waitForTransactionReceipt).toHaveBeenCalledWith({
hash,
confirmations: confirmations[Network.MAINNET],
timeout: 0,
checkReplacement: true,
});
});
});

describe('Safe App', () => {
const safeWaitForTransactionReceipt = jest.fn<
Promise<TransactionReceipt>,
[{ hash: Hash; confirmations: number }]
>();
const extend = jest.fn(() => ({
waitForTransactionReceipt: safeWaitForTransactionReceipt,
}));

it('should wait for the receipt through the Safe provider when connected as a Safe', async () => {
mockUseWallet.mockReturnValue({
account,
active: true,
activate: jest.fn(),
deactivate: jest.fn(),
chainId: 1,
isGnosisSafe: true,
publicClient: { waitForTransactionReceipt },
walletClient: { extend },
} as any);
const hash = buildTransactionHash();
safeWaitForTransactionReceipt.mockResolvedValue(
buildContractReceipt(),
);
const { result } = renderHook(() => useTransaction<string>());

const promise = new Promise<Hash>((resolve) =>
act(() => resolve(hash)),
);

act(() => {
result.current.set(promise);
});

await waitFor(() => expect(result.current.state).toBe('confirmed'));

expect(result.current.receipt).toBeDefined();
expect(waitForTransactionReceipt).not.toHaveBeenCalled();
expect(safeWaitForTransactionReceipt).toHaveBeenCalledWith({
hash,
confirmations: confirmations[Network.MAINNET],
timeout: 0,
checkReplacement: false,
});
});

it('should fall back to the public client when the Safe wallet client is not ready', async () => {
mockUseWallet.mockReturnValue({
account,
active: true,
activate: jest.fn(),
deactivate: jest.fn(),
chainId: 1,
isGnosisSafe: true,
publicClient: { waitForTransactionReceipt },
walletClient: undefined,
} as any);
const hash = buildTransactionHash();
waitForTransactionReceipt.mockResolvedValue(buildContractReceipt());
const { result } = renderHook(() => useTransaction<string>());

const promise = new Promise<Hash>((resolve) =>
act(() => resolve(hash)),
);

act(() => {
result.current.set(promise);
});

await waitFor(() => expect(result.current.state).toBe('confirmed'));

expect(extend).not.toHaveBeenCalled();
expect(waitForTransactionReceipt).toHaveBeenCalledWith(
expect.objectContaining({ hash, checkReplacement: false }),
);
});
});

describe('Error stages', () => {
it('should change its state to errored when transaction is rejected for any reason', async () => {
const { result } = renderHook(() => useTransaction<string>());
Expand Down
4 changes: 2 additions & 2 deletions __tests__/utils/messages.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ describe('useMessages hook', () => {
const { result } = renderHook(() =>
useMessages('node.authorize.success'),
);
expect(result.current).toEqual('Node authorized with success!');
expect(result.current).toEqual('Node authorized successfully!');
});

describe('PoS v2 migration steps', () => {
Expand Down Expand Up @@ -296,7 +296,7 @@ describe('useMessages hook', () => {
useMessages('pool.update.v2.success'),
);
expect(result.current).toEqual(
'Pool updated to use PoS v2 with success!',
'Pool updated to use PoS v2 successfully!',
);
});

Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@chakra-ui/react": "^3.37.0",
"@emotion/react": "^11.14.0",
"@libsql/client": "^0.18.0",
"@next/third-parties": "^16.4.1",
"@rainbow-me/rainbowkit": "^2.2.11",
"@supercharge/promise-pool": "3.3.0",
"@tanstack/react-query": "^5.104.0",
Expand All @@ -51,7 +52,6 @@
"react-chartjs-2": "^5.3.1",
"react-dom": "^19.3.0",
"react-ga4": "^3.0.1",
"react-gtm-module": "^2.0.11",
"react-hook-form": "^7.88.0",
"react-icons": "^5.7.0",
"react-is": "^19.3.0",
Expand Down Expand Up @@ -85,7 +85,6 @@
"@types/node": "^24",
"@types/react": "^19.3.0",
"@types/react-dom": "^19.3.0",
"@types/react-gtm-module": "^2.0.4",
"@typescript-eslint/eslint-plugin": "^8.70.0",
"@typescript-eslint/parser": "^8.70.0",
"@wagmi/cli": "^2.10.0",
Expand Down
36 changes: 20 additions & 16 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/components/node/NodeManage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ const ManageNode: FC = () => {
<TransactionInfoBanner
title="Withdrawing..."
failTitle="Error withdrawing"
successDescription="Withdrawed successfully."
successDescription="Withdrawn successfully."
transaction={withdrawTransaction}
onClose={() => setWithdrawAlertActive(false)}
/>
Expand All @@ -422,7 +422,7 @@ const ManageNode: FC = () => {
<TransactionInfoBanner
title="Staking..."
failTitle="Error staking"
successDescription="Stake set successfully."
successDescription="Staked successfully."
transaction={stakeTransaction}
onClose={() => setStakeAlertActive(false)}
/>
Expand Down
2 changes: 1 addition & 1 deletion src/components/node/steps/HireNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ const HireNode = ({
<TransactionBanner
title="Hiring the node..."
failTitle="Hiring the node failed"
successDescription="Node hired! moving to the next step..."
successDescription="Node hired! Moving to the next step..."
transaction={node.transaction}
/>
<NodeInput
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ const CommissionModel = ({
<TransactionBanner
title="Creating the pool..."
failTitle="The pool creation failed!"
successDescription={`Pool ${poolFactory.transaction?.result} created! moving to the next step...`}
successDescription={`Pool ${poolFactory.transaction?.result} created! Moving to the next step...`}
transaction={poolFactory.transaction}
mb={2}
/>
Expand Down
2 changes: 1 addition & 1 deletion src/components/pools/steps/ENS.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ const EthereumNameServer = ({
return (
<Step
title="Pool ENS"
subtitle="Registering a ENS domain and setting it up."
subtitle="Registering an ENS domain and setting it up."
stepNumber={stepNumber}
status={stepState.status}
onActive={onStepActive}
Expand Down
2 changes: 1 addition & 1 deletion src/components/pools/steps/HireNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const wordingFor = {
hire: {
title: 'Hiring node...',
failTitle: 'Hiring the node failed',
successDescription: 'Node hired! moving to the next step...',
successDescription: 'Node hired! Moving to the next step...',
},
};

Expand Down
4 changes: 2 additions & 2 deletions src/components/stake/Staking.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export const Staking: FC<StakingProps> = ({
<TransactionBanner
title="Withdrawing..."
failTitle="Error withdrawing"
successDescription="Withdrawed successfully."
successDescription="Withdrawn successfully."
transaction={withdrawTransaction}
/>

Expand All @@ -164,7 +164,7 @@ export const Staking: FC<StakingProps> = ({
<TransactionBanner
title="Staking..."
failTitle="Error staking"
successDescription="Stake set successfully."
successDescription="Staked successfully."
transaction={stakeTransaction}
/>

Expand Down
Loading
Loading