diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..888d42d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/forge-std"] + path = lib/forge-std + url = https://github.com/foundry-rs/forge-std diff --git a/README.md b/README.md index f63c082..5545342 100644 --- a/README.md +++ b/README.md @@ -1,205 +1,189 @@ -# Foundry Template [![Open in Gitpod][gitpod-badge]][gitpod] [![Github Actions][gha-badge]][gha] [![Foundry][foundry-badge]][foundry] [![License: MIT][license-badge]][license] +# Hyperlane LSP7 version - `HypLSP7` [![Open in Gitpod][gitpod-badge]][gitpod] [![Github Actions][gha-badge]][gha] [![Foundry][foundry-badge]][foundry] [![License: MIT][license-badge]][license] -[gitpod]: https://gitpod.io/#https://github.com/PaulRBerg/foundry-template +[gitpod]: https://gitpod.io/#https://github.com/lukso-network/lsp-bridge-HypLSP7 [gitpod-badge]: https://img.shields.io/badge/Gitpod-Open%20in%20Gitpod-FFB45B?logo=gitpod -[gha]: https://github.com/PaulRBerg/foundry-template/actions -[gha-badge]: https://github.com/PaulRBerg/foundry-template/actions/workflows/ci.yml/badge.svg +[gha]: https://github.com/lukso-network/lsp-bridge-HypLSP7/actions +[gha-badge]: https://github.com/lukso-network/lsp-bridge-HypLSP7/actions/workflows/ci.yml/badge.svg [foundry]: https://getfoundry.sh/ [foundry-badge]: https://img.shields.io/badge/Built%20with-Foundry-FFDB1C.svg [license]: https://opensource.org/licenses/MIT [license-badge]: https://img.shields.io/badge/License-MIT-blue.svg -A Foundry-based template for developing Solidity smart contracts, with sensible defaults. +## Architecture & Workflow -## What's Inside +The flow for bridging tokens is generally as follow: -- [Forge](https://github.com/foundry-rs/foundry/blob/master/forge): compile, test, fuzz, format, and deploy smart - contracts -- [Forge Std](https://github.com/foundry-rs/forge-std): collection of helpful contracts and utilities for testing -- [Prettier](https://github.com/prettier/prettier): code formatter for non-Solidity files -- [Solhint](https://github.com/protofire/solhint): linter for Solidity code +- if the token is originally from ETH, the token is locked on ETHEREUM, and minted on LUKSO. +- if the token is originally from LUKSO, the token is burnt on LUKSO, minted on ETHEREUM. -## Getting Started +### Ethereum -> LUKSO -Click the [`Use this template`](https://github.com/PaulRBerg/foundry-template/generate) button at the top of the page to -create a new repository with this repo as the initial state. +![Ethereum to LUKSO bridge flow](./assets/flow-ethereum-lukso-hashi-bridge.png) -Or, if you prefer to install the template manually: -```sh -$ mkdir my-project -$ cd my-project -$ forge init --template PaulRBerg/foundry-template -$ bun install # install Solhint, Prettier, and other Node.js deps -``` +**on Ethereum chain** -If this is your first time with Foundry, check out the -[installation](https://github.com/foundry-rs/foundry#installation) instructions. +1. User transfer ERC20 tokens to [`HypERC20Collateral`]. This locks the tokens in the collateral contract. +2. `HypERC20Collateral` contract call [`Mailbox`] to pass the message. +3. The `Mailbox` calls: + 3.1. the default Hook (created by Hyperlane), + 3.2. and the Hashi Hook (created by CCIA team). +4. Hashi Hook dispatch the token relaying message from `Yaho` contracts. -## Features +> In the architecture diagram above: +> - The `Yaho` contracts handle the dispatching and batching of messages across chains. +> - The `Yaru` contracts ensures that the messages are properly executed on the destination chain by calling relevant functions like `onMessage`. -This template builds upon the frameworks and libraries mentioned above, so please consult their respective documentation -for details about their specific features. -For example, if you're interested in exploring Foundry in more detail, you should look at the -[Foundry Book](https://book.getfoundry.sh/). In particular, you may be interested in reading the -[Writing Tests](https://book.getfoundry.sh/forge/writing-tests.html) tutorial. -### Sensible Defaults +**Off chain** -This template comes with a set of sensible default configurations for you to use. These defaults can be found in the -following files: +5. Hashi relayer (managed by CCIA team) listen for events from `Yaho` contracts and request the reporter contracts to relay token relaying message. +6. Hashi executor (managed by CCIA team) listen to event from each Hashi adapter contracts and call `Yaru.executeMessages`. **This step checks whether the Hashi adapters agree on a specify message id** (a threshold number of hash is stored), and set the message Id to verified status. +7. Validator (run by Hyperlane & LUKSO team) will sign the Merkle root when new dispatches happen in Mailbox. +8. Hyperlane relayer (run by Hyperlane team) relays the message by calling Mailbox.process(). -```text -├── .editorconfig -├── .gitignore -├── .prettierignore -├── .prettierrc.yml -├── .solhint.json -├── foundry.toml -└── remappings.txt -``` +**on LUKSO chain** -### VSCode Integration +8. When [`Mailbox.process(...)`](https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/3d116132b87d36af9576d6b116f31a53d680db4a/solidity/contracts/Mailbox.sol#L188-L197) is called, it will: + 8.1. check with Multisig ISM (includes Hashi ISM), whether the message is signed by validators & verified by Hashi ISM. + 8.2. If so, it will mint [HypLSP7](./src/HypLSP7.sol) tokens to the receiver. -This template is IDE agnostic, but for the best user experience, you may want to use it in VSCode alongside Nomic -Foundation's [Solidity extension](https://marketplace.visualstudio.com/items?itemName=NomicFoundation.hardhat-solidity). -For guidance on how to integrate a Foundry project in VSCode, please refer to this -[guide](https://book.getfoundry.sh/config/vscode). +### LUKSO -> Ethereum -### GitHub Actions +![LUKSO to Ethereum bridge flow](./assets/flow-lukso-ethereum-hashi-bridge.png) -This template comes with GitHub Actions pre-configured. Your contracts will be linted and tested on every push and pull -request made to the `main` branch. +**on LUKSO chain** -You can edit the CI script in [.github/workflows/ci.yml](./.github/workflows/ci.yml). +> _Step 1 to 3 needs to be confirmed_ -## Installing Dependencies +1. User transfer LSP7 token to HypLSP7 contract and the tokens are burnt. +2. HypLSP7 contract calls `Mailbox` to pass the message. +3. `Mailbox` calls Default Hook (created by Hyperlane) and Hashi Hook (created by CCIA team). +4. Hashi Hook dispatch the token relaying message from Yaho contracts. -Foundry typically uses git submodules to manage dependencies, but this template uses Node.js packages because -[submodules don't scale](https://twitter.com/PaulRBerg/status/1736695487057531328). +**Off chain** -This is how to install dependencies: +4. Off chain process remains the same as before, _except there is no Light Client support for Hashi from LUKSO → Ethereum_. -1. Install the dependency using your preferred package manager, e.g. `bun install dependency-name` - - Use this syntax to install from GitHub: `bun install github:username/repo-name` -2. Add a remapping for the dependency in [remappings.txt](./remappings.txt), e.g. - `dependency-name=node_modules/dependency-name` +**on Ethereum chain** -Note that OpenZeppelin Contracts is pre-installed, so you can follow that as an example. +5. When `Mailbox.process()` is called: + 5.1. it will check with Multisig ISM (includes Hashi ISM), whether the message is signed by validators & verified by Hashi ISM. + 5.2. If so, it will unlock ERC20 token to the receiver on the Ethereum chain. -## Writing Tests -To write a new test contract, you start by importing `Test` from `forge-std`, and then you inherit it in your test -contract. Forge Std comes with a pre-instantiated [cheatcodes](https://book.getfoundry.sh/cheatcodes/) environment -accessible via the `vm` property. If you would like to view the logs in the terminal output, you can add the `-vvv` flag -and use [console.log](https://book.getfoundry.sh/faq?highlight=console.log#how-do-i-use-consolelog). -This template comes with an example test contract [Foo.t.sol](./test/Foo.t.sol) +### Examples of bridged tokens -## Usage +- ETH -> LUKSO: https://explorer.hyperlane.xyz/message/0x53a383e32fdb68748c8af5c86be3669e58eadc377db2a9f420826cb9474dd55c -This is a list of the most frequently needed commands. +- LUKSO -> ETH: https://explorer.hyperlane.xyz/message/0xf9c86a22e7b5584fc87a9d4ffc39f967a8745cd28b98ed2eaeb220c43996c4ca -### Build -Build the contracts: +### Relevant links & resources -```sh -$ forge build -``` +- [Cross Chain Alliance - Hashi](https://crosschain-alliance.gitbook.io/hashi) +- [Hyperlane smart contracts monorepo](https://github.com/hyperlane-xyz/hyperlane-monorepo) -### Clean +## Getting Started -Delete the build artifacts and cache directories: +This project is based on the Foundry template by PaulRBerg. ```sh -$ forge clean +bun install # install Solhint, Prettier, and other Node.js deps ``` -### Compile +If this is your first time with Foundry, check out the +[installation](https://github.com/foundry-rs/foundry#installation) instructions. -Compile the contracts: +### Installing Dependencies -```sh -$ forge build -``` +Foundry typically uses git submodules to manage dependencies, but this template uses Node.js packages because +[submodules don't scale](https://twitter.com/PaulRBerg/status/1736695487057531328). -### Coverage +This is how to install dependencies: -Get a test coverage report: +1. Install the dependency using your preferred package manager, e.g. `bun install dependency-name` + - Use this syntax to install from GitHub: `bun install github:username/repo-name` +2. Add a remapping for the dependency in [remappings.txt](./remappings.txt), e.g. + `dependency-name=node_modules/dependency-name` -```sh -$ forge coverage -``` +Note that OpenZeppelin Contracts is pre-installed, so you can follow that as an example. -### Deploy -Deploy to Anvil: -```sh -$ forge script script/Deploy.s.sol --broadcast --fork-url http://localhost:8545 +### Sensible Defaults + +This template comes with a set of sensible default configurations for you to use. These defaults can be found in the +following files: + +```text +├── .editorconfig +├── .gitignore +├── .prettierignore +├── .prettierrc.yml +├── .solhint.json +├── foundry.toml +└── remappings.txt ``` -For this script to work, you need to have a `MNEMONIC` environment variable set to a valid -[BIP39 mnemonic](https://iancoleman.io/bip39/). -For instructions on how to deploy to a testnet or mainnet, check out the -[Solidity Scripting](https://book.getfoundry.sh/tutorials/solidity-scripting.html) tutorial. -### Format -Format the contracts: +## Usage -```sh -$ forge fmt -``` +This is a list of the most frequently needed commands. -### Gas Usage +### Build & Compile -Get a gas report: ```sh -$ forge test --gas-report -``` +# Build the contracts: +forge build -### Lint +# Delete the build artifacts and cache directories: +forge clean -Lint the contracts: +# Get a test coverage report: +forge coverage -```sh -$ bun run lint -``` +# Format the contracts: +forge fmt -### Test +# Get a gas report: +forge test --gas-report -Run the tests: +# Lint the contracts: +bun run lint -```sh -$ forge test -``` +# Run the tests: +forge test -Generate test coverage and output result to the terminal: +# Generate test coverage and output result to the terminal: +bun run test:coverage -```sh -$ bun run test:coverage +# Generate test coverage with lcov report (you'll have to open the `./coverage/index.html` file in your browser, +# to do so simply copy paste the path): +bun run test:coverage:report ``` -Generate test coverage with lcov report (you'll have to open the `./coverage/index.html` file in your browser, to do so -simply copy paste the path): +### GitHub Actions -```sh -$ bun run test:coverage:report -``` +This repository uses pre-configured GitHub Actions. The contracts are linted and tested on every push and pull requests. + +You can edit the CI script in [.github/workflows/ci.yml](./.github/workflows/ci.yml). -## Related Efforts -- [abigger87/femplate](https://github.com/abigger87/femplate) -- [cleanunicorn/ethereum-smartcontract-template](https://github.com/cleanunicorn/ethereum-smartcontract-template) -- [foundry-rs/forge-template](https://github.com/foundry-rs/forge-template) -- [FrankieIsLost/forge-template](https://github.com/FrankieIsLost/forge-template) +## Foundry Resources + +This template builds upon the frameworks and libraries mentioned above, so please consult their respective documentation for details about their specific features. + +For example, if you're interested in exploring Foundry in more detail, you should look at the +[Foundry Book](https://book.getfoundry.sh/). In particular, you may be interested in reading the +[Writing Tests](https://book.getfoundry.sh/forge/writing-tests.html) tutorial. -## License -This project is licensed under MIT. +[`HypERC20Collateral`]: https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/%40hyperlane-xyz/core%405.2.0/solidity/contracts/token/HypERC20Collateral.sol +[`Mailbox`]: https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/%40hyperlane-xyz/core%405.2.0/solidity/contracts/Mailbox.sol diff --git a/assets/flow-ethereum-lukso-hashi-bridge.png b/assets/flow-ethereum-lukso-hashi-bridge.png new file mode 100644 index 0000000..99b38eb Binary files /dev/null and b/assets/flow-ethereum-lukso-hashi-bridge.png differ diff --git a/assets/flow-lukso-ethereum-hashi-bridge.png b/assets/flow-lukso-ethereum-hashi-bridge.png new file mode 100644 index 0000000..f790b79 Binary files /dev/null and b/assets/flow-lukso-ethereum-hashi-bridge.png differ diff --git a/bun.lockb b/bun.lockb index 86d5c93..c00deba 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/erc725-smart-contracts-v8-rc0.tgz b/erc725-smart-contracts-v8-rc0.tgz new file mode 100644 index 0000000..b63c818 Binary files /dev/null and b/erc725-smart-contracts-v8-rc0.tgz differ diff --git a/lib/forge-std b/lib/forge-std new file mode 160000 index 0000000..1714bee --- /dev/null +++ b/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 1714bee72e286e73f76e320d110e0eaf5c4e649d diff --git a/lukso-lsp4-contracts-0.16.0-rc.0.tgz b/lukso-lsp4-contracts-0.16.0-rc.0.tgz new file mode 100644 index 0000000..1da6efe Binary files /dev/null and b/lukso-lsp4-contracts-0.16.0-rc.0.tgz differ diff --git a/lukso-lsp7-contracts-0.16.0-rc.0.tgz b/lukso-lsp7-contracts-0.16.0-rc.0.tgz new file mode 100644 index 0000000..974d1b2 Binary files /dev/null and b/lukso-lsp7-contracts-0.16.0-rc.0.tgz differ diff --git a/package.json b/package.json index 80569b6..dacc335 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,16 @@ { - "name": "@prb/foundry-template", - "description": "Foundry-based template for developing Solidity smart contracts", + "name": "@lukso-network/lsp-bridge-HypLSP7", + "description": "", "version": "1.0.0", "author": { - "name": "Paul Razvan Berg", - "url": "https://github.com/PaulRBerg" + "name": "lukso-network", + "url": "https://github.com/lukso-network" }, "dependencies": { - "@openzeppelin/contracts": "^5.0.1" + "@erc725/smart-contracts-v8": "erc725-smart-contracts-v8-rc0.tgz", + "@hyperlane-xyz/core": "^5.0.0", + "@lukso/lsp4-contracts": "lukso-lsp4-contracts-0.16.0-rc.0.tgz", + "@lukso/lsp7-contracts": "lukso-lsp7-contracts-0.16.0-rc.0.tgz" }, "devDependencies": { "forge-std": "github:foundry-rs/forge-std#v1.8.1", diff --git a/remappings.txt b/remappings.txt index 550f908..e59c580 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,2 +1,2 @@ -@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/ +@/=node_modules/@ forge-std/=node_modules/forge-std/ diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index 498db52..22f5f9c 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -1,13 +1,12 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.25 <0.9.0; -import { Foo } from "../src/Foo.sol"; import { BaseScript } from "./Base.s.sol"; /// @dev See the Solidity Scripting tutorial: https://book.getfoundry.sh/tutorials/solidity-scripting contract Deploy is BaseScript { - function run() public broadcast returns (Foo foo) { - foo = new Foo(); - } + // function run() public broadcast returns (Foo foo) { + // foo = new Foo(); + // } } diff --git a/src/Foo.sol b/src/Foo.sol deleted file mode 100644 index 7483070..0000000 --- a/src/Foo.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.25; - -contract Foo { - function id(uint256 value) external pure returns (uint256) { - return value; - } -} diff --git a/src/HypLSP7.sol b/src/HypLSP7.sol new file mode 100644 index 0000000..cd4cd60 --- /dev/null +++ b/src/HypLSP7.sol @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.19; + +// modules +import { LSP7DigitalAssetInitAbstract } from "@lukso/lsp7-contracts/contracts/LSP7DigitalAssetInitAbstract.sol"; +import { TokenRouter } from "@hyperlane-xyz/core/contracts/token/libs/TokenRouter.sol"; +import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +// libraries +import { TokenMessageForLSP7 } from "./TokenMessageForLSP7.sol"; + +/** + * @title LSP7 version of the Hyperlane ERC20 Token Router + * @dev https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/main/solidity/contracts/token/HypERC20.sol + */ +contract HypLSP7 is LSP7DigitalAssetInitAbstract, TokenRouter { + uint8 private immutable _decimals; + + constructor(uint8 __decimals, address _mailbox) TokenRouter(_mailbox) { + _decimals = __decimals; + } + + /** + * @notice Initializes the Hyperlane router, LSP7 metadata, and mints initial supply to deployer. + * @param _totalSupply The initial supply of the token. + * @param _name The name of the token. + * @param _symbol The symbol of the token. + */ + function initialize( + uint256 _totalSupply, + string memory _name, + string memory _symbol, + address _hook, + address _interchainSecurityModule, + address _owner, + uint256 _lsp4TokenType + ) + external + initializer + { + // Initialize LSP7 metadata + LSP7DigitalAssetInitAbstract._initialize({ + name_: _name, + symbol_: _symbol, + newOwner_: _owner, + lsp4TokenType_: _lsp4TokenType, + isNonDivisible_: false // isNonDivisible set to `false` as will not be used anyway since decimals() is + // overriden + }); + + // mints initial supply to deployer + LSP7DigitalAssetInitAbstract._mint({ to: msg.sender, amount: _totalSupply, force: true, data: "" }); + + // Initializes the Hyperlane router + _MailboxClient_initialize(_hook, _interchainSecurityModule, _owner); + } + + function decimals() public view virtual override returns (uint8) { + return _decimals; + } + + function balanceOf(address _account) + public + view + virtual + override(TokenRouter, LSP7DigitalAssetInitAbstract) + returns (uint256) + { + return LSP7DigitalAssetInitAbstract.balanceOf(_account); + } + + /** + * @dev Burns `_amount` of token from `msg.sender` balance. + * @inheritdoc TokenRouter + */ + function _transferFromSender(uint256 _amount) internal override returns (bytes memory) { + LSP7DigitalAssetInitAbstract._burn(msg.sender, _amount, ""); + return bytes(""); // no metadata + } + + /** + * @dev Mints `_amount` of token to `_recipient` balance. + * @inheritdoc TokenRouter + */ + function _transferTo( + address _recipient, + uint256 _amount, + bytes calldata // no metadata + ) + internal + virtual + override + { + LSP7DigitalAssetInitAbstract._mint(_recipient, _amount, true, ""); + } + + function _transferRemote( + uint32 _destination, + bytes32 _recipient, + uint256 _amountOrId, + uint256 _value, + bytes memory _hookMetadata, + address _hook + ) + internal + virtual + override(TokenRouter) + returns (bytes32 messageId) + { + bytes memory _tokenMetadata = _transferFromSender(_amountOrId); + bytes memory _tokenMessage = TokenMessageForLSP7.format(_recipient, _amountOrId, _tokenMetadata); + + messageId = _Router_dispatch(_destination, _value, _tokenMessage, _hookMetadata, _hook); + + emit SentTransferRemote(_destination, _recipient, _amountOrId); + } +} diff --git a/src/TokenMessageForLSP7.sol b/src/TokenMessageForLSP7.sol new file mode 100644 index 0000000..5da1ebb --- /dev/null +++ b/src/TokenMessageForLSP7.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity >=0.8.0; + +/// @dev Adjusted version of the TokenMessage library from Hyperlane +/// to extract parameters from the calldata of an LSP7 transfer +/// according to the `transfer(address,address,uint256,bool,bytes)` signature. +library TokenMessageForLSP7 { + function format(bytes32 _recipient, uint256 _amount, bytes memory _metadata) internal view returns (bytes memory) { + return abi.encodePacked( + msg.sender, // TODO: which sender should be specified here? Should we add an extra parameter? + _recipient, + _amount, + true, // force param set to `true` by default + _metadata + ); + } + + function recipient(bytes calldata message) internal pure returns (bytes32) { + return bytes32(message[32:64]); + } + + function amount(bytes calldata message) internal pure returns (uint256) { + return uint256(bytes32(message[64:96])); + } +} diff --git a/test/Foo.t.sol b/test/Foo.t.sol deleted file mode 100644 index 727337a..0000000 --- a/test/Foo.t.sol +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.25 <0.9.0; - -import { Test } from "forge-std/src/Test.sol"; -import { console2 } from "forge-std/src/console2.sol"; - -import { Foo } from "../src/Foo.sol"; - -interface IERC20 { - function balanceOf(address account) external view returns (uint256); -} - -/// @dev If this is your first time with Forge, read this tutorial in the Foundry Book: -/// https://book.getfoundry.sh/forge/writing-tests -contract FooTest is Test { - Foo internal foo; - - /// @dev A function invoked before each test case is run. - function setUp() public virtual { - // Instantiate the contract-under-test. - foo = new Foo(); - } - - /// @dev Basic test. Run it with `forge test -vvv` to see the console log. - function test_Example() external view { - console2.log("Hello World"); - uint256 x = 42; - assertEq(foo.id(x), x, "value mismatch"); - } - - /// @dev Fuzz test that provides random values for an unsigned integer, but which rejects zero as an input. - /// If you need more sophisticated input validation, you should use the `bound` utility instead. - /// See https://twitter.com/PaulRBerg/status/1622558791685242880 - function testFuzz_Example(uint256 x) external view { - vm.assume(x != 0); // or x = bound(x, 1, 100) - assertEq(foo.id(x), x, "value mismatch"); - } - - /// @dev Fork test that runs against an Ethereum Mainnet fork. For this to work, you need to set `API_KEY_ALCHEMY` - /// in your environment You can get an API key for free at https://alchemy.com. - function testFork_Example() external { - // Silently pass this test if there is no API key. - string memory alchemyApiKey = vm.envOr("API_KEY_ALCHEMY", string("")); - if (bytes(alchemyApiKey).length == 0) { - return; - } - - // Otherwise, run the test against the mainnet fork. - vm.createSelectFork({ urlOrAlias: "mainnet", blockNumber: 16_428_000 }); - address usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; - address holder = 0x7713974908Be4BEd47172370115e8b1219F4A5f0; - uint256 actualBalance = IERC20(usdc).balanceOf(holder); - uint256 expectedBalance = 196_307_713.810457e6; - assertEq(actualBalance, expectedBalance); - } -}