Skip to content
Draft
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
2 changes: 2 additions & 0 deletions .changeset/beige-sails-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
40 changes: 22 additions & 18 deletions contracts/.gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
ExaAccountFactoryTest:testFuzz_createAccount_EOAOwners(uint256,address[63]) (runs: 256, μ: 5853309, ~: 5347535)
ExaAccountFactoryTest:testFuzz_createAccount_EOAOwners(uint256,address[63]) (runs: 256, μ: 5920045, ~: 5447138)
ExaAccountFactoryTest:test_deploy_deploysToSameAddress() (gas: 13989314)
ExaPluginTest:testFork_claimAndVestEscrowedEXA_claimsAndVests() (gas: 38664724)
ExaPluginTest:testFork_collectCollateral_collects() (gas: 32167249)
ExaPluginTest:testFork_crossRepay_repays() (gas: 33988652)
ExaPluginTest:testFork_repay_whenFlashLoanerHasFees() (gas: 25624949)
ExaPluginTest:testFork_stakeEXA_stakes() (gas: 32744199)
ExaPluginTest:testFork_swap_swaps() (gas: 28109827)
ExaPluginTest:testFork_claimAndVestEscrowedEXA_claimsAndVests() (gas: 38674070)
ExaPluginTest:testFork_collectCollateral_collects() (gas: 32175991)
ExaPluginTest:testFork_crossRepay_repays() (gas: 33997394)
ExaPluginTest:testFork_repay_whenFlashLoanerHasFees() (gas: 25628545)
ExaPluginTest:testFork_stakeEXA_stakes() (gas: 32753198)
ExaPluginTest:testFork_swap_swaps() (gas: 28117771)
ExaPluginTest:test_allowPlugin_emitsPluginAllowed() (gas: 58464)
ExaPluginTest:test_allowPlugin_reverts_whenAddressZero() (gas: 32411)
ExaPluginTest:test_allowPlugin_reverts_whenNotAdmin() (gas: 40964)
Expand All @@ -32,12 +32,12 @@ ExaPluginTest:test_collectCredit_collects_whenHealthFactorHigherThanOne() (gas:
ExaPluginTest:test_collectCredit_collects_whenProposalCausesInsufficientLiquidity() (gas: 1064982)
ExaPluginTest:test_collectCredit_collects_whenProposalLeavesHealthFactorLowerThanOne() (gas: 1055379)
ExaPluginTest:test_collectCredit_collects_withEnoughSlippage() (gas: 782246)
ExaPluginTest:test_collectCredit_collects_withPrevIssuerSignature() (gas: 986021)
ExaPluginTest:test_collectCredit_collects_withPrevIssuerSignature() (gas: 986192)
ExaPluginTest:test_collectCredit_passes_whenProposalLeavesEnoughLiquidity() (gas: 1082579)
ExaPluginTest:test_collectCredit_reverts_asNotKeeper() (gas: 342148)
ExaPluginTest:test_collectCredit_reverts_whenDisagreement() (gas: 538817)
ExaPluginTest:test_collectCredit_reverts_whenExpired() (gas: 352767)
ExaPluginTest:test_collectCredit_reverts_whenPrevSignatureNotValidAnymore() (gas: 482934)
ExaPluginTest:test_collectCredit_reverts_whenPrevSignatureNotValidAnymore() (gas: 483105)
ExaPluginTest:test_collectCredit_reverts_whenReplay() (gas: 825827)
ExaPluginTest:test_collectCredit_reverts_whenTimelocked() (gas: 334568)
ExaPluginTest:test_collectCredit_toleratesTimeDrift() (gas: 798846)
Expand All @@ -58,7 +58,7 @@ ExaPluginTest:test_collectInstallments_reverts_whenTimelocked() (gas: 336703)
ExaPluginTest:test_collectInstallments_toleratesTimeDrift() (gas: 1161587)
ExaPluginTest:test_collect_collects_whenProposalsLeaveNoLiquidity() (gas: 1783636)
ExaPluginTest:test_collect_collects_whenTooMuchProposedDebt() (gas: 1923835)
ExaPluginTest:test_crossRepay_avoidsFrozenDeposit() (gas: 981381)
ExaPluginTest:test_crossRepay_avoidsFrozenDeposit() (gas: 981551)
ExaPluginTest:test_crossRepay_avoidsZeroSharesDeposit() (gas: 1394086)
ExaPluginTest:test_crossRepay_consumesProposal() (gas: 2205189)
ExaPluginTest:test_crossRepay_repays() (gas: 2268979)
Expand Down Expand Up @@ -202,6 +202,10 @@ ExaPreviewerTest:test_collect_reverts_whenProposalsLeaveNoLiquidity() (gas: 1437
ExaPreviewerTest:test_markets_returnsMarkets() (gas: 150447)
ExaPreviewerTest:test_pendingProposals_returnsPendingProposals() (gas: 1984093)
ExaPreviewerTest:test_utilizations_returns() (gas: 134922)
HypEXATest:test_handle_reverts_withoutBridgeRole() (gas: 122511)
HypEXATest:test_roundTrip_opToBaseToOp() (gas: 1046501)
HypEXATest:test_roundTrip_opToPolygonToBaseToOp() (gas: 1549147)
HypEXATest:test_transferRemote_reverts_withoutBridgeRole() (gas: 230258)
IssuerCheckerTest:test_setIssuer_emits_IssuerSet() (gas: 82935)
IssuerCheckerTest:test_setIssuer_reverts_whenNotAdmin() (gas: 43708)
IssuerCheckerTest:test_setIssuer_reverts_whenZeroAddress() (gas: 35130)
Expand All @@ -212,14 +216,14 @@ IssuerCheckerTest:test_setPrevIssuerWindow_emits_PrevIssuerWindowSet() (gas: 526
IssuerCheckerTest:test_setPrevIssuerWindow_reverts_whenNotAdmin() (gas: 45548)
MockSwapperTest:test_swapExactAmountIn_swaps() (gas: 269807)
MockSwapperTest:test_swapExactAmountOut_swaps() (gas: 269803)
RedeployerTest:test_deployEXA_deploysAtSameAddress_onBase() (gas: 56250957)
RedeployerTest:test_deployExaFactory_deploysAtSameAddress_onEthereum() (gas: 273369114)
RedeployerTest:test_deployExaFactory_deploysAtSameAddress_onPolygon() (gas: 368043624)
RedeployerTest:test_deployExaFactory_deploysViaCreate3AtSameAddress_onPolygon() (gas: 45014545)
RedeployerTest:test_prepare_reverts_whenAdminIsDeployer() (gas: 28800535)
RedeployerTest:test_recoversNativeETHOnPolygon() (gas: 45185670)
RedeployerTest:test_run_reverts_whenAttackerUpgradesProxy() (gas: 38475794)
RedeployerTest:test_run_reverts_whenTargetNonceTooLow() (gas: 29265310)
RedeployerTest:test_deployEXA_deploysAtSameAddress_onBase() (gas: 60018226)
RedeployerTest:test_deployExaFactory_deploysAtSameAddress_onEthereum() (gas: 276636412)
RedeployerTest:test_deployExaFactory_deploysAtSameAddress_onPolygon() (gas: 371310355)
RedeployerTest:test_deployExaFactory_deploysViaCreate3AtSameAddress_onPolygon() (gas: 48280428)
RedeployerTest:test_prepare_reverts_whenAdminIsDeployer() (gas: 32066081)
RedeployerTest:test_recoversNativeETHOnPolygon() (gas: 48451625)
RedeployerTest:test_run_reverts_whenAttackerUpgradesProxy() (gas: 42900849)
RedeployerTest:test_run_reverts_whenTargetNonceTooLow() (gas: 32530983)
RefunderTest:test_refund_refunds() (gas: 263363)
RefunderTest:test_refund_reverts_whenExpired() (gas: 88359)
RefunderTest:test_refund_reverts_whenNotKeeper() (gas: 68861)
Expand Down
5 changes: 5 additions & 0 deletions contracts/deploy.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
"84532": "0xE4F8C9904a79EBd88Ff73ed3F86a3CFd1b489eDf",
"11155420": "0x3E2D4b69C52932CB5b2a9Ee744CB585bb201c771",
"default": "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE"
},
"mailbox": {
"10": "0xd4C1905BB1D26BC93DAC913e13CaCC278CdCC80D",
"137": "0x5d934f4e2f797775e53561bB72aca21ba36B96BB",
"8453": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D"
Comment on lines +42 to +45
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider adding a mailbox default (or explicit unsupported-chain guard).

The acct("mailbox") path falls back to .accounts.mailbox.default when a chain key is absent; without it, unsupported chains fail with a generic JSON key error instead of a clearer deployment-time failure.

}
},
"issuerChecker": { "operationExpiry": 4838400, "prevIssuerWindow": 4838400 },
Expand Down
3 changes: 2 additions & 1 deletion contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@
"webauthn-sol": "base/webauthn-sol#v1.0.0"
},
"devDependencies": {
"@exactly/protocol": "^0.2.22",
"@exactly/protocol": "exactly/protocol#5833408",
"@openzeppelin/contracts-upgradeable-v4": "npm:@openzeppelin/contracts-upgradeable@^4.9.6",
"@openzeppelin/contracts-upgradeable": "^5.4.0",
"@openzeppelin/contracts-v4": "npm:@openzeppelin/contracts@^4.9.6",
"@hyperlane-xyz/core": "^9.0.0",
"account-abstraction": "eth-infinitism/account-abstraction#v0.6.0",
"forge-std": "foundry-rs/forge-std#v1.14.0",
"fresh-crypto-lib": "rdubois-crypto/FreshCryptoLib#fd2a0e6e64609ade0b0c165e4d66de8106a70db6",
Expand Down
2 changes: 2 additions & 0 deletions contracts/remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ webauthn-owner-plugin/=node_modules/webauthn-owner-plugin/src
webauthn-sol/=node_modules/webauthn-sol/src

@exactly/protocol/=node_modules/@exactly/protocol/contracts/
node_modules/@exactly/protocol/:@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/
@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable-v4/
@openzeppelin/contracts-upgradeable-v4/=node_modules/@openzeppelin/contracts-upgradeable-v4/
@openzeppelin/contracts-v4/=node_modules/@openzeppelin/contracts-v4/
@openzeppelin/contracts/=node_modules/@openzeppelin/contracts-v4/
@hyperlane-xyz/=node_modules/@hyperlane-xyz/
account-abstraction/=node_modules/account-abstraction/contracts/
forge-std/=node_modules/forge-std/src/
solmate/=node_modules/solmate/
Expand Down
52 changes: 51 additions & 1 deletion contracts/script/Redeployer.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import { WebauthnOwnerPlugin } from "webauthn-owner-plugin/WebauthnOwnerPlugin.s

import { EXA } from "@exactly/protocol/periphery/EXA.sol";

import { HypERC20Collateral } from "@hyperlane-xyz/core/contracts/token/HypERC20Collateral.sol";
import { HypXERC20 } from "@hyperlane-xyz/core/contracts/token/extensions/HypXERC20.sol";

import { ExaAccountFactory } from "../src/ExaAccountFactory.sol";
import {
ExaPlugin,
Expand Down Expand Up @@ -90,9 +93,56 @@ contract Redeployer is BaseScript {

/// @notice Deploys EXA token and upgrades the proxy to it.
function deployEXA(address proxy) external {
vm.startBroadcast(acct("admin"));
address admin = acct("admin");
vm.startBroadcast(admin);
exa = EXA(CREATE3_FACTORY.deploy(keccak256(abi.encode("EXA")), vm.getCode("EXA.sol:EXA")));
proxyAdmin.upgradeAndCall(ITransparentUpgradeableProxy(proxy), address(exa), abi.encodeCall(EXA.initialize, ()));
proxyAdmin.upgradeAndCall(
ITransparentUpgradeableProxy(proxy), address(exa), abi.encodeCall(EXA.initialize2, (admin))
);
vm.stopBroadcast();
}

/// @notice Deploys the latest EXA implementation via CREATE3.
function deployEXAImpl() external {
vm.broadcast(acct("admin"));
exa = EXA(CREATE3_FACTORY.deploy(keccak256(abi.encode("EXA")), vm.getCode("EXA.sol:EXA")));
}

/// @notice Upgrades an existing EXA proxy to the latest implementation.
function upgradeEXA(address proxy) external {
address admin = acct("admin");
ProxyAdmin p =
ProxyAdmin(address(uint160(uint256(vm.load(proxy, bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1))))));
vm.broadcast(p.owner());
p.upgradeAndCall(ITransparentUpgradeableProxy(proxy), address(exa), abi.encodeCall(EXA.initialize2, (admin)));
}

function deployRouter(address token) external returns (HypXERC20 router) {
address admin = acct("admin");
vm.startBroadcast(admin);
router = HypXERC20(
CREATE3_FACTORY.deploy(
keccak256(abi.encode("HypEXA")),
abi.encodePacked(
type(TransparentUpgradeableProxy).creationCode,
abi.encode(
address(new HypXERC20(token, 1, acct("mailbox"))),
address(proxyAdmin),
abi.encodeCall(HypERC20Collateral.initialize, (address(0), address(0), admin))
)
Comment on lines +121 to +133
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Where proxyAdmin deployment is currently validated:"
rg -nP 'address\(proxyAdmin\)\.code\.length' contracts/script/Redeployer.s.sol

echo
echo "run() guard context:"
sed -n '71,75p' contracts/script/Redeployer.s.sol

echo
echo "deployRouter() context:"
sed -n '121,138p' contracts/script/Redeployer.s.sol

Repository: exactly/exa

Length of output: 1088


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Full deployRouter() function signature:"
sed -n '121,121p' contracts/script/Redeployer.s.sol

echo
echo "Check for other callers of deployRouter in the file:"
rg -n 'deployRouter' contracts/script/Redeployer.s.sol

echo
echo "Check if proxyAdmin is set before deployRouter calls:"
sed -n '70,85p' contracts/script/Redeployer.s.sol

Repository: exactly/exa

Length of output: 1056


Add proxyAdmin code-length guard to deployRouter() to prevent proxy deployment with undeployed admin.

The run() function validates address(proxyAdmin).code.length at line 73, but deployRouter() is a separate external entry point that uses address(proxyAdmin) at line 131 without the same check. Since deployRouter() can be called independently, the proxy admin validation must be present here as well.

🛠️ Proposed fix
 function deployRouter(address token) external returns (HypXERC20 router) {
   address admin = acct("admin");
+  if (address(proxyAdmin).code.length == 0) revert ProxyAdminNotDeployed();
   vm.startBroadcast(admin);
   router = HypXERC20(
     CREATE3_FACTORY.deploy(
       keccak256(abi.encode("HypEXA")),

)
)
);
vm.stopBroadcast();
}

function setupRouter(address token, uint32 remoteDomain) external {
address admin = acct("admin");
address router = CREATE3_FACTORY.getDeployed(admin, keccak256(abi.encode("HypEXA")));
vm.startBroadcast(admin);
if (!EXA(token).hasRole(keccak256("BRIDGE_ROLE"), router)) EXA(token).grantRole(keccak256("BRIDGE_ROLE"), router);
HypXERC20(router).enrollRemoteRouter(remoteDomain, bytes32(uint256(uint160(router))));
vm.stopBroadcast();
}

Expand Down
174 changes: 174 additions & 0 deletions contracts/test/HypEXA.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.0;

import { EXA } from "@exactly/protocol/periphery/EXA.sol";
import { TypeCasts } from "@hyperlane-xyz/core/contracts/libs/TypeCasts.sol";
import { HypXERC20 } from "@hyperlane-xyz/core/contracts/token/extensions/HypXERC20.sol";

import { Redeployer } from "../script/Redeployer.s.sol";
import { ForkTest } from "./Fork.t.sol";

contract HypEXATest is ForkTest {
using TypeCasts for address;

uint256 internal opFork;
uint256 internal baseFork;
uint256 internal polygonFork;
HypXERC20 internal opRouter;
HypXERC20 internal baseRouter;
HypXERC20 internal polygonRouter;
address internal admin;
address internal opMailbox;
address internal baseMailbox;
address internal polygonMailbox;
EXA internal exa = EXA(0x1e925De1c68ef83bD98eE3E130eF14a50309C01B);
address internal exaHolder = 0x92024C4bDa9DA602b711B9AbB610d072018eb58b;

uint32 internal constant OP_DOMAIN = 10;
uint32 internal constant BASE_DOMAIN = 8453;
uint32 internal constant POLYGON_DOMAIN = 137;

function setUp() external {
polygonFork = vm.createSelectFork("polygon", 83_700_000);
polygonMailbox = acct("mailbox");
Redeployer polygonRedeployer = new Redeployer();
polygonRedeployer.setUp();
if (address(polygonRedeployer.proxyAdmin()).code.length == 0) polygonRedeployer.prepare();
polygonRedeployer.run(polygonRedeployer.findNonce(acct("deployer"), address(exa), 1000) + 1);
polygonRedeployer.deployEXA(address(exa));
polygonRouter = polygonRedeployer.deployRouter(address(exa));
polygonRedeployer.setupRouter(address(exa), OP_DOMAIN);
polygonRedeployer.setupRouter(address(exa), BASE_DOMAIN);

baseFork = vm.createSelectFork("base", 42_380_000);
baseMailbox = acct("mailbox");
Redeployer baseRedeployer = new Redeployer();
baseRedeployer.setUp();
if (address(baseRedeployer.proxyAdmin()).code.length == 0) baseRedeployer.prepare();
baseRedeployer.run(baseRedeployer.findNonce(acct("deployer"), address(exa), 1000) + 1);
baseRedeployer.deployEXA(address(exa));
baseRouter = baseRedeployer.deployRouter(address(exa));
baseRedeployer.setupRouter(address(exa), OP_DOMAIN);
baseRedeployer.setupRouter(address(exa), POLYGON_DOMAIN);

opFork = vm.createSelectFork("optimism", 147_967_000);
opMailbox = acct("mailbox");
admin = acct("admin");
Redeployer opRedeployer = new Redeployer();
opRedeployer.setUp();
opRedeployer.deployEXAImpl();
opRedeployer.upgradeEXA(address(exa));
opRouter = opRedeployer.deployRouter(address(exa));
opRedeployer.setupRouter(address(exa), BASE_DOMAIN);
opRedeployer.setupRouter(address(exa), POLYGON_DOMAIN);
}

// solhint-disable func-name-mixedcase

function test_roundTrip_opToBaseToOp() external {
address receiver = makeAddr("receiver");
uint256 amount = 100e18;
uint256 opSupply = exa.totalSupply();

uint256 fee = opRouter.quoteGasPayment(BASE_DOMAIN);
vm.deal(exaHolder, fee);
vm.prank(exaHolder);
opRouter.transferRemote{ value: fee }(BASE_DOMAIN, exaHolder.addressToBytes32(), amount);
assertEq(exa.totalSupply(), opSupply - amount, "op didn't burn");

vm.selectFork(baseFork);
vm.prank(baseMailbox);
baseRouter.handle(
OP_DOMAIN, address(opRouter).addressToBytes32(), abi.encodePacked(exaHolder.addressToBytes32(), amount)
);
assertEq(exa.balanceOf(exaHolder), amount, "base didn't credit holder");
assertEq(exa.totalSupply(), amount, "base didn't mint");

fee = baseRouter.quoteGasPayment(OP_DOMAIN);
vm.deal(exaHolder, fee);
vm.prank(exaHolder);
baseRouter.transferRemote{ value: fee }(OP_DOMAIN, receiver.addressToBytes32(), amount);
assertEq(exa.totalSupply(), 0, "base didn't burn");

vm.selectFork(opFork);
vm.prank(opMailbox);
opRouter.handle(
BASE_DOMAIN, address(baseRouter).addressToBytes32(), abi.encodePacked(receiver.addressToBytes32(), amount)
);
assertEq(exa.balanceOf(receiver), amount, "op didn't credit receiver");
assertEq(exa.totalSupply(), opSupply, "op didn't restore supply");
}

function test_roundTrip_opToPolygonToBaseToOp() external {
uint256 amount = 100e18;
uint256 opSupply = exa.totalSupply();

uint256 fee = opRouter.quoteGasPayment(POLYGON_DOMAIN);
vm.deal(exaHolder, fee);
vm.prank(exaHolder);
opRouter.transferRemote{ value: fee }(POLYGON_DOMAIN, exaHolder.addressToBytes32(), amount);
assertEq(exa.totalSupply(), opSupply - amount, "op didn't burn");

vm.selectFork(polygonFork);
uint256 polygonSupply = exa.totalSupply();
vm.prank(polygonMailbox);
polygonRouter.handle(
OP_DOMAIN, address(opRouter).addressToBytes32(), abi.encodePacked(exaHolder.addressToBytes32(), amount)
);
assertEq(exa.totalSupply(), polygonSupply + amount, "polygon didn't mint");

fee = polygonRouter.quoteGasPayment(BASE_DOMAIN);
vm.deal(exaHolder, fee);
vm.prank(exaHolder);
polygonRouter.transferRemote{ value: fee }(BASE_DOMAIN, exaHolder.addressToBytes32(), amount);
assertEq(exa.totalSupply(), polygonSupply, "polygon didn't burn");

vm.selectFork(baseFork);
uint256 baseSupply = exa.totalSupply();
vm.prank(baseMailbox);
baseRouter.handle(
POLYGON_DOMAIN, address(polygonRouter).addressToBytes32(), abi.encodePacked(exaHolder.addressToBytes32(), amount)
);
assertEq(exa.totalSupply(), baseSupply + amount, "base didn't mint");

fee = baseRouter.quoteGasPayment(OP_DOMAIN);
vm.deal(exaHolder, fee);
vm.prank(exaHolder);
baseRouter.transferRemote{ value: fee }(OP_DOMAIN, exaHolder.addressToBytes32(), amount);
assertEq(exa.totalSupply(), baseSupply, "base didn't burn");

vm.selectFork(opFork);
vm.prank(opMailbox);
opRouter.handle(
BASE_DOMAIN, address(baseRouter).addressToBytes32(), abi.encodePacked(exaHolder.addressToBytes32(), amount)
);
assertEq(exa.totalSupply(), opSupply, "op didn't restore supply");
}

function test_transferRemote_reverts_withoutBridgeRole() external {
vm.prank(admin);
exa.revokeRole(keccak256("BRIDGE_ROLE"), address(opRouter));

uint256 fee = opRouter.quoteGasPayment(BASE_DOMAIN);
vm.deal(exaHolder, fee);
vm.prank(exaHolder);
vm.expectRevert();
opRouter.transferRemote{ value: fee }(BASE_DOMAIN, makeAddr("receiver").addressToBytes32(), 100e18);
}

function test_handle_reverts_withoutBridgeRole() external {
vm.selectFork(baseFork);
vm.prank(admin);
exa.revokeRole(keccak256("BRIDGE_ROLE"), address(baseRouter));

vm.prank(baseMailbox);
vm.expectRevert();
baseRouter.handle(
OP_DOMAIN,
address(opRouter).addressToBytes32(),
abi.encodePacked(makeAddr("receiver").addressToBytes32(), uint256(100e18))
);
}

// solhint-enable func-name-mixedcase
}
3 changes: 1 addition & 2 deletions contracts/test/Redeployer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ contract RedeployerTest is ForkTest {
EXA token = EXA(exaOP);
assertEq(token.name(), "exactly");
assertEq(token.symbol(), "EXA");
assertEq(token.totalSupply(), 10_000_000e18, "token should have same minted supply");
assertEq(token.balanceOf(address(redeployer.proxyAdmin())), 10_000_000e18, "ProxyAdmin should have tokens");
assertEq(token.totalSupply(), 0, "base should have zero supply");
assertEq(token.decimals(), 18, "token should have 18 decimals");
}

Expand Down
2 changes: 2 additions & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
"hexlify",
"hideable",
"hono",
"hyperlane",
"IBMPlexMono-Medm",
"IERC",
"indoc",
Expand Down Expand Up @@ -187,6 +188,7 @@
"worklet",
"worklets",
"xcrun",
"XERC",
"xhdpi",
"xxhdpi",
"xxxhdpi",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@
"react-native-worklets"
]
},
"neverBuiltDependencies": [],
"onlyBuiltDependencies": ["@exactly/protocol"],
"peerDependencyRules": {
"allowedVersions": {
"typescript": "$typescript"
Expand Down
Loading
Loading