v3.0.0-alpha.7
Pre-releaseMajor Changes
-
Replace the generic data availability solution with an explicit input box contract (#537)
- Replace
getDataAvailability()(returningbytes) withgetInputBox()(returningIInputBox) inIApplication - Replace the
bytes dataAvailabilityparameter with anIInputBox inputBoxparameter in theApplicationconstructor, inIApplicationFactory(newApplication,calculateApplicationAddress) and inISelfHostedApplicationFactory(deployContracts,calculateAddresses) - Replace the
bytes dataAvailabilityfield with anIInputBox inputBoxfield in theApplicationCreatedevent - Remove the
DataAvailabilitylibrary
- Replace
-
Make portals read the input box from the application contract instead of holding it as an immutable
- Remove the
getInputBox()function fromIPortaland theIInputBoxparameter from every portal constructor - Make
IPortalinherit fromIApplicationChecker, because deposits (since 3.0.0-alpha.2) may revert withApplicationNotDeployed,ApplicationRevertedorIllformedApplicationReturnData - Add an
InputBoxNotDeployederror toIApplicationChecker, raised when the input box advertised by the application has no code - As a consequence, a deposit is now routed to the input box chosen by the application, instead of the one hard-wired into the portal
- Remove the
-
Change the
submitClaimfunction to prove that the post-epoch machine is manually yielded with anrx acceptedreason (#538)- Replace the
bytes32 outputsMerkleRootandbytes32[] proofparameters withbytes32 machineMerkleRootandMachineValidityProof proofinIConsensus.submitClaim(implemented byAuthorityandQuorum) - Add the
MachineValidityProofstruct, which bundles threeLeafProofstructs proving theiflags_Yregister, the HTIFtohostregister and the first data block of the CMIO tx buffer - Add the
LeafProofstruct (a 32-byte data block plus its bottom-up siblings) - Remove the
InvalidOutputsMerkleRootProofSizeerror fromIConsensus, and makeIConsensusinherit from the newMachineValidationErrorsinterface instead, which definesInvalidSiblingsArrayLength,InvalidMachineMerkleProof,InvalidPostEpochMachineIflagsYRegisterandInvalidPostEpochMachineHtifTohostRegister - A machine that fails these checks may have reached an unrecoverable state, in which case the application should be foreclosed so that users can recover funds through emergency withdrawals and deposit refunds
- Replace the
-
Require the account owner to occupy the last 20 bytes of every encoded account (#540)
- Account encodings remain application-specific, but must now end with the account owner address encoded as a 20-byte big-endian string, so that the node can extract owners from the accounts drive and serve owner-to-account-index lookups
- Change the USD account encoding accordingly: the balance is now a
uint96(up fromuint64) stored little-endian in the first 12 bytes, and the owner address occupies the last 20 bytes - Require USD accounts to be exactly 32 bytes long (there is no more tail padding, and the account must fit in a single data block)
- Replace the
AccountTooShort(uint64 attemptedAccountSize, uint64 minAccountSize)error withInvalidAccountSize(uint256 attemptedAccountSize, uint64 accountSize)inIWithdrawalOutputBuilderErrors
-
Treat
ERCas a regular word incamelCaseandPascalCaseidentifiersThis is a mechanical rename (replace
ERCwithErcin Cartesi-owned identifiers) motivated by Forge's Rust binding generation, which turnedIERC20Portalintoierc20_portalinstead ofi_erc20_portal. Definitions imported from OpenZeppelin are unaffected.- Contracts:
ERC20Portal,ERC721Portal,ERC1155SinglePortal,ERC1155BatchPortalandSafeERC20TransferbecomeErc20Portal,Erc721Portal,Erc1155SinglePortal,Erc1155BatchPortalandSafeErc20Transfer - Interfaces:
IERC20Portal,IERC721Portal,IERC1155SinglePortal,IERC1155BatchPortalandISafeERC20TransferbecomeIErc20Portal,IErc721Portal,IErc1155SinglePortal,IErc1155BatchPortalandISafeErc20Transfer - Deposit functions:
depositERC20Tokens,depositERC721Token,depositSingleERC1155TokenanddepositBatchERC1155TokenbecomedepositErc20Tokens,depositErc721Token,depositSingleErc1155TokenanddepositBatchErc1155Token InputEncodingfunctions:encodeERC20Deposit,encodeERC721Deposit,encodeSingleERC1155DepositandencodeBatchERC1155DepositbecomeencodeErc20Deposit,encodeErc721Deposit,encodeSingleErc1155DepositandencodeBatchErc1155Deposit- Errors:
ERC20TransferFailedbecomesErc20TransferFailed
- Contracts:
-
Remove the
IApplicationForeclosureandIApplicationWithdrawalinterfaces, moving all of their definitions intoIApplicationThe individual events, errors and functions are unchanged, but clients importing these two interfaces directly must now import
IApplicationinstead.IApplicationalso inherits fromAddressErrors,BinaryMerkleTreeErrors,IRefundOutputBuilderErrorsandIWithdrawalOutputBuilderErrors. -
Reject deposits of fee-on-transfer ERC-20 tokens
Some non-compliant ERC-20 tokens charge a fee per transfer, so the recipient balance grows by less than the transfer value. The ERC-20 portal now compares the application balance before and after the transfer and reverts unless the delta matches the deposited value exactly. This prevents the application from believing it holds more tokens than it does on the base layer, which would otherwise lead to insolvency and to withdrawal and refund outputs that cannot be executed.
- Add the
Erc20TransferDecreasedApplicationBalance(uint256 balanceBefore, uint256 balanceAfter)andErc20TransferValueIsNotBalanceDelta(uint256 value, uint256 balanceDelta)errors toIErc20Portal
- Add the
-
Restrict consensus migration to the deployment block
migrateToOutputsMerkleRootValidatornow reverts with the newNotDeploymentBlockerror if called in any block other than the one in which the application was deployed. This protects users from application owners who could otherwise take control of locked funds by swapping the outputs Merkle root validator; the owner now serves merely as an implementation detail that lets factories deploy application-consensus pairs in a single transaction. -
Remove the
appOwnerparameter fromISelfHostedApplicationFactory.deployContractsandcalculateAddressesThe factory now deploys the application under its own ownership and immediately renounces it, so self-hosted applications are ownerless from the start and can no longer migrate to another outputs Merkle root validator.
-
Add a
wasInputFinalized(address appContract, uint256 inputIndex, uint256 blockNumber)function toIOutputsMerkleRootValidatorImplementers of this interface outside of this repository must implement the new function.
AbstractConsensus(and thereforeAuthorityandQuorum) implements it by comparing the block number against the application's first unprocessed block number. -
Add an
IRefundOutputBuilderparameter to theApplicationFactoryconstructorThe refund output builder is a factory-wide immutable rather than a per-application parameter, so it is not part of
WithdrawalConfigand is not passed tonewApplication. -
Apply the checks-effects-interactions pattern to
executeOutput,issueRefundandwithdraw, and remove theReentrancyGuardThis is cheaper than acquiring and releasing a reentrancy lock (one fewer storage read and write), but it changes what on-chain observers see during the interaction:
wasOutputExecuted,wasRefundForInputIssuedandwereAccountFundsWithdrawnnow returntrue, and the corresponding events are emitted, before the output is executed. Off-chain components such as the Cartesi Rollups Node are unaffected. -
Index application contract event parameters
OutputExecuted,RefundIssuedandWithdrawalnow declare their index parameter (outputIndex,inputIndexandaccountIndex, respectively) asindexed, which changes the event topic layout and allows filtering by index. This adds a negligible gas cost toexecuteOutput,issueRefundandwithdraw. -
Revert output execution when the target account has no code
- Add the
TargetHasNoCode(address target)error to the newAddressErrorsinterface, inherited byIApplication - Raise it from
executeOutputwhen a voucher with a non-empty payload or a delegate-call voucher target an account with no code - Move the
InsufficientFundserror fromIApplicationtoAddressErrors(still reachable throughIApplication)
This can indicate a programming error (the back-end emitted an executable output targetting to the wrong address) or an operational one (the target was never deployed to the target network). If the target can still be deployed to the expected address, the application can be fixed on the fly; otherwise, foreclosure is the best alternative.
- Add the
-
Make
foreclose()revert withForeclosed()if the application has already been foreclosed (#534)As a result, an application emits the
Foreclosure()event at most once. -
Discontinue npm and Cannon distribution (#531)
- Stop publishing the
@cartesi/rollupspackage to npmjs.com, and removepackage.json, the pnpm lockfile and the pnpm/corepack dependency - Remove
cannonfile.tomland all Cannon support - Remove the
.changesetdirectory; changelogs are now written manually on every release - Define the project version in
src/common/Version.sol(generated from theMakefile) instead of inpackage.json - The contracts source code remains available through the Soldeer package and its artifacts through GitHub releases
- Stop publishing the
-
Restrict the published build artifacts to a curated subset of contracts (#539)
The artifacts tarball and the Rust bindings crate now contain only the contracts that clients (
rollups-ts,rollups-explorer,rollups-node) are expected to use. Artifacts for dependencies (OpenZeppelin, Machine Solidity Step), test utilities and internal libraries are no longer published. Concrete deployed contracts are included alongside their interfaces so thatrollups-tscan generate wagmi hooks, but using interfaces is recommended wherever possible. -
Bump the Solidity pragma of all contracts to
^0.8.30 -
Bump Foundry from 1.4.3 to 1.5.1
Minor Changes
-
Add deposit refunds, which let users recover assets from unprocessed deposits after an application is foreclosed (#512)
- Add the
RefundOutputBuildercontract (along with theIRefundOutputBuilderandIRefundOutputBuilderErrorsinterfaces), which decodes a deposit input and builds an output transferring the asset back to the original depositor. It is static-called by the application, and supports Ether, ERC-20, ERC-721 and single and batch ERC-1155 deposits made through the canonical portals. It is deployed as a core contract on all supported networks. - Add an
issueRefund(uint256 inputIndex, bytes input)function toIApplication, callable by anyone once the application is foreclosed, for inputs that were never finalized. On success it marks the input as refunded, emits aRefundIssued(uint256 indexed inputIndex, bytes input, bytes output)event, and executes the refund output. - Add
getRefundOutputBuilder,getNumberOfIssuedRefundsandwasRefundForInputIssuedview functions toIApplication - Add
validateInput(uint256 inputIndex, bytes input)andvalidateInputHash(uint256 inputIndex, bytes32 inputHash)view functions toIApplication, which check an input against the application's input box and decode it - Add the
CannotRefundFinalizedInput,RefundAlreadyIssued,InvalidInputIndex,InvalidInputHashandIllFormedInputerrors toIApplication, and theUnknownInputSendererror toIRefundOutputBuilderErrors(raised for non-deposit inputs or inputs from non-canonical portals) - Add decoding counterparts to
InputEncoding(decodeEtherDeposit,decodeErc20Deposit,decodeErc721Deposit,decodeErc1155SingleDeposit,decodeErc1155BatchDeposit) along with the corresponding deposit structs and libraries - Document in the portal interfaces that a refund may fail if the depositor is a contract that does not accept the asset back (for example, a smart contract wallet with no
receiveentrypoint, or one that does not implement the ERC-721/ERC-1155 receiver hooks); in that case the funds may not be recoverable
- Add the
-
Support emergency withdrawals even in the absence of accepted claims (#530)
In
proveAccountsDriveMerkleRoot, if the outputs Merkle root validator reports a zeroed last-finalized machine Merkle root, the application's template hash is used instead. This covers the edge case in which the accounts drive is not initially empty. -
Make
AuthorityandQuorumreturntruefromsupportsInterfacefor theIOutputsMerkleRootValidatorinterface ID -
Deploy a
UsdWithdrawalOutputBuilderto devnet (#532)It is deployed through the
UsdWithdrawalOutputBuilderFactorywithTestFungibleTokenas the backing ERC-20 token, and stored asTestUsdWithdrawalOutputBuilderto make it clear that it is devnet-only. -
Add recipient-taking mint and burn entrypoints to the devnet test tokens
TestFungibleToken:mint(address to, uint256 value)andburn(uint256 value), compatible with thecast erc20 mintandcast erc20 burncommands introduced in Foundry 1.5.0TestNonFungibleToken:mint(address to, uint256 tokenId)TestMultiToken:mint(address to, uint256 tokenId, uint256 value)andmintBatch(address to, uint256[] tokenIds, uint256[] values)
-
Add
testdirectory to published Soldeer package
Patch Changes
- Bump
cartesi-machine-solidity-stepfrom 0.13.0 to 0.15.0-test1 (a further bump is expected once a definitive 0.15.0 tag is released) - Bump the Foundry toolchain action from 1.3.1 to 1.8.0
- Refactor the deployment pipeline: replace the Bash scripts with a
Makefile, add a code-generation Forge script that emits typed contract deployers (script/utils/ContractDeployers.sol) and the version constants, add a CI job that checks the generated code is up to date, and setalways_use_create_2_factory(#529) - Reuse the deployment script code in the tests, and drop the
SimpleERC*helpers in favour of the devnetTest*Tokencontracts (#528) - Add
Makefiletargets:install-foundry,check-foundry-version(a prerequisite ofdevnet, so a state dump is never produced with the wrong Foundry version),coverage,rust-bindings,publish-soldeer-package,release-artifacts,deploy-livenetsandcodegen - Build the RPC URL for each chain automatically when the
ALCHEMY_API_KEYenvironment variable is set, and rename the networks to match the Alchemy subdomains - Add the
buildtarget as a prerequisite of every deployment target, so parallel deployments do not race to fetch the Solidity compiler or rebuild redundantly - Check that a refund has not already been issued before validating the input, so
issueRefundreverts earlier in that case - Check whether an output was already executed before building it, since the execution check has a bounded cost and is more susceptible to race conditions, while output builders behave almost as pure functions
- Annotate the assembly blocks in
LibKeccak256,LibErrorandLibAddressas memory-safe - Move the helper
ExternalLibBinaryMerkleTreelibrary out of the test file and rename itLibBinaryKeccak256MerkleTree, making the hash function explicit - Turn on the
fmt.single_line_importsFoundry option, introduced in Foundry 1.5.0 - Fix the documentation on
LibWithdrawalConfig(#509) - Fix the ERC-20 portal documentation, which claimed a custom error is raised when the token returns an empty or ill-formed value instead of an ABI-encoded boolean; in reality Solidity type-checks it and raises a low-level error
- Fix the deployment documentation on the Ethereum mainnet RPC URL environment variable and Make target
- Fix the README, which described Dave as a future plan even though it has long been implemented and integrated through the
DaveConsensuscontract - Fix a typo in
ISafeErc20Transfer, theErc1155BatchDepositNatSpec, and assorted minor documentation issues - Remove the unused gas-optimization, smart-contract-audit and update-dependencies issue templates, and drop the
T-*labels - Improve test quality and coverage: fuzz application deployment arguments in the factory tests, test ill-formed inputs via a mocked input box, test ERC-1155 batch deposits with zero, one and many token IDs separately, test refunds with ill-formed payloads and reverting ERC-20 transfers, test outputs targeting contracts that reject assets, test re-execution attempts through all three entrypoints, and define custom errors for internal test failures
Full Changelog: v3.0.0-alpha.6...v3.0.0-alpha.7