diff --git a/contracts/HubPool.sol b/contracts/HubPool.sol index 6abf70110..c9ec370d0 100644 --- a/contracts/HubPool.sol +++ b/contracts/HubPool.sol @@ -147,8 +147,8 @@ contract HubPool is HubPoolInterface, Testable, Lockable, MultiCaller, Ownable { bool depositsEnabled ); event ProposeRootBundle( - uint32 requestExpirationTimestamp, - uint64 unclaimedPoolRebalanceLeafCount, + uint32 challengePeriodEndTimestamp, + uint64 poolRebalanceLeafCount, uint256[] bundleEvaluationBlockNumbers, bytes32 indexed poolRebalanceRoot, bytes32 indexed relayerRefundRoot, @@ -159,10 +159,10 @@ contract HubPool is HubPoolInterface, Testable, Lockable, MultiCaller, Ownable { uint256 groupIndex, uint256 indexed leafId, uint256 indexed chainId, - address[] l1Token, + address[] l1Tokens, uint256[] bundleLpFees, - int256[] netSendAmount, - int256[] runningBalance, + int256[] netSendAmounts, + int256[] runningBalances, address indexed caller ); event SpokePoolAdminFunctionTriggered(uint256 indexed chainId, bytes message); @@ -558,11 +558,11 @@ contract HubPool is HubPoolInterface, Testable, Lockable, MultiCaller, Ownable { // technically valid but not useful. This could also potentially be enforced at the UMIP-level. require(poolRebalanceLeafCount > 0, "Bundle must have at least 1 leaf"); - uint32 requestExpirationTimestamp = uint32(getCurrentTime()) + liveness; + uint32 challengePeriodEndTimestamp = uint32(getCurrentTime()) + liveness; delete rootBundleProposal; // Only one bundle of roots can be executed at a time. - rootBundleProposal.requestExpirationTimestamp = requestExpirationTimestamp; + rootBundleProposal.challengePeriodEndTimestamp = challengePeriodEndTimestamp; rootBundleProposal.unclaimedPoolRebalanceLeafCount = poolRebalanceLeafCount; rootBundleProposal.poolRebalanceRoot = poolRebalanceRoot; rootBundleProposal.relayerRefundRoot = relayerRefundRoot; @@ -573,7 +573,7 @@ contract HubPool is HubPoolInterface, Testable, Lockable, MultiCaller, Ownable { bondToken.safeTransferFrom(msg.sender, address(this), bondAmount); emit ProposeRootBundle( - requestExpirationTimestamp, + challengePeriodEndTimestamp, poolRebalanceLeafCount, bundleEvaluationBlockNumbers, poolRebalanceRoot, @@ -610,7 +610,7 @@ contract HubPool is HubPoolInterface, Testable, Lockable, MultiCaller, Ownable { address[] memory l1Tokens, bytes32[] memory proof ) public nonReentrant unpaused { - require(getCurrentTime() > rootBundleProposal.requestExpirationTimestamp, "Not passed liveness"); + require(getCurrentTime() > rootBundleProposal.challengePeriodEndTimestamp, "Not passed liveness"); // Verify the leafId in the poolRebalanceLeaf has not yet been claimed. require(!MerkleLib.isClaimed1D(rootBundleProposal.claimedBitMap, leafId), "Already claimed"); @@ -702,7 +702,7 @@ contract HubPool is HubPoolInterface, Testable, Lockable, MultiCaller, Ownable { */ function disputeRootBundle() public nonReentrant zeroOptimisticOracleApproval { uint32 currentTime = uint32(getCurrentTime()); - require(currentTime <= rootBundleProposal.requestExpirationTimestamp, "Request passed liveness"); + require(currentTime <= rootBundleProposal.challengePeriodEndTimestamp, "Request passed liveness"); // Request price from OO and dispute it. uint256 finalFee = _getBondTokenFinalFee(); @@ -948,10 +948,12 @@ contract HubPool is HubPoolInterface, Testable, Lockable, MultiCaller, Ownable { // that will flow from L2 to L1. In this case, we can use it normally in the equation. However, if it is // negative, then it is already counted in liquidReserves. This occurs if tokens are transferred directly to the // contract. In this case, ignore it as it is captured in liquid reserves and has no meaning in the numerator. - PooledToken memory pooledToken = pooledTokens[l1Token]; // Note this is storage so the state can be modified. - uint256 flooredUtilizedReserves = pooledToken.utilizedReserves > 0 ? uint256(pooledToken.utilizedReserves) : 0; + PooledToken memory pooledL1Token = pooledTokens[l1Token]; + uint256 flooredUtilizedReserves = pooledL1Token.utilizedReserves > 0 + ? uint256(pooledL1Token.utilizedReserves) // If positive: take the uint256 cast utilizedReserves. + : 0; // Else, if negative, then the is already captured in liquidReserves and should be ignored. uint256 numerator = relayedAmount + flooredUtilizedReserves; - uint256 denominator = pooledToken.liquidReserves + flooredUtilizedReserves; + uint256 denominator = pooledL1Token.liquidReserves + flooredUtilizedReserves; // If the denominator equals zero, return 1e18 (max utilization). if (denominator == 0) return 1e18; diff --git a/contracts/HubPoolInterface.sol b/contracts/HubPoolInterface.sol index 8953669c1..5011baa67 100644 --- a/contracts/HubPoolInterface.sol +++ b/contracts/HubPoolInterface.sol @@ -49,7 +49,7 @@ interface HubPoolInterface { // - Send funds from a SpokePool to Relayer as a refund for a relayed deposit // - Send funds from a SpokePool to a deposit recipient to fulfill a "slow" relay // Anyone can dispute this struct if the merkle roots contain invalid leaves before the - // requestExpirationTimestamp. Once the expiration timestamp is passed, executeRootBundle to execute a leaf + // challengePeriodEndTimestamp. Once the expiration timestamp is passed, executeRootBundle to execute a leaf // from the poolRebalanceRoot on this contract and it will simultaneously publish the relayerRefundRoot and // slowRelayRoot to a SpokePool. The latter two roots, once published to the SpokePool, contain // leaves that can be executed on the SpokePool to pay relayers or recipients. @@ -68,7 +68,7 @@ interface HubPoolInterface { // of leaves are executed, a new root bundle can be proposed uint8 unclaimedPoolRebalanceLeafCount; // When root bundle challenge period passes and this root bundle becomes executable. - uint32 requestExpirationTimestamp; + uint32 challengePeriodEndTimestamp; } // Each whitelisted L1 token has an associated pooledToken struct that contains all information used to track the @@ -131,7 +131,7 @@ interface HubPoolInterface { function liquidityUtilizationCurrent(address l1Token) external returns (uint256); - function liquidityUtilizationPostRelay(address token, uint256 relayedAmount) external returns (uint256); + function liquidityUtilizationPostRelay(address l1Token, uint256 relayedAmount) external returns (uint256); function sync(address l1Token) external; diff --git a/contracts/LpTokenFactory.sol b/contracts/LpTokenFactory.sol index 777558b55..f947805b1 100644 --- a/contracts/LpTokenFactory.sol +++ b/contracts/LpTokenFactory.sol @@ -17,10 +17,11 @@ contract LpTokenFactory is LpTokenFactoryInterface { */ function createLpToken(address l1Token) public returns (address) { ExpandedERC20 lpToken = new ExpandedERC20( - _append("Across V2 ", IERC20Metadata(l1Token).name(), " LP Token"), // LP Token Name - _append("Av2-", IERC20Metadata(l1Token).symbol(), "-LP"), // LP Token Symbol + _concatenate("Across V2 ", IERC20Metadata(l1Token).name(), " LP Token"), // LP Token Name + _concatenate("Av2-", IERC20Metadata(l1Token).symbol(), "-LP"), // LP Token Symbol IERC20Metadata(l1Token).decimals() // LP Token Decimals ); + lpToken.addMinter(msg.sender); // Set the caller as the LP Token's minter. lpToken.addBurner(msg.sender); // Set the caller as the LP Token's burner. lpToken.resetOwner(msg.sender); // Set the caller as the LP Token's owner. @@ -28,7 +29,7 @@ contract LpTokenFactory is LpTokenFactoryInterface { return address(lpToken); } - function _append( + function _concatenate( string memory a, string memory b, string memory c diff --git a/contracts/Optimism_SpokePool.sol b/contracts/Optimism_SpokePool.sol index 0776bd65d..03b11f940 100644 --- a/contracts/Optimism_SpokePool.sol +++ b/contracts/Optimism_SpokePool.sol @@ -74,7 +74,7 @@ contract Optimism_SpokePool is CrossDomainEnabled, SpokePool { * ETH over the canonical token bridge instead of WETH. * @inheritdoc SpokePool */ - function executeSlowRelayRoot( + function executeSlowRelayLeaf( address depositor, address recipient, address destinationToken, @@ -86,9 +86,9 @@ contract Optimism_SpokePool is CrossDomainEnabled, SpokePool { uint32 rootBundleId, bytes32[] memory proof ) public override(SpokePool) nonReentrant { - if (destinationToken == address(weth)) _depositEthToWeth(); + if (destinationToken == address(wrappedNativeToken)) _depositEthToWeth(); - _executeSlowRelayRoot( + _executeSlowRelayLeaf( depositor, recipient, destinationToken, @@ -108,14 +108,14 @@ contract Optimism_SpokePool is CrossDomainEnabled, SpokePool { * ETH over the canonical token bridge instead of WETH. * @inheritdoc SpokePool */ - function executeRelayerRefundRoot( + function executeRelayerRefundLeaf( uint32 rootBundleId, SpokePoolInterface.RelayerRefundLeaf memory relayerRefundLeaf, bytes32[] memory proof ) public override(SpokePool) nonReentrant { - if (relayerRefundLeaf.l2TokenAddress == address(weth)) _depositEthToWeth(); + if (relayerRefundLeaf.l2TokenAddress == address(wrappedNativeToken)) _depositEthToWeth(); - _executeRelayerRefundRoot(rootBundleId, relayerRefundLeaf, proof); + _executeRelayerRefundLeaf(rootBundleId, relayerRefundLeaf, proof); } /************************************** @@ -127,13 +127,13 @@ contract Optimism_SpokePool is CrossDomainEnabled, SpokePool { // this logic inside a fallback method that executes when this contract receives ETH because ETH is an ERC20 // on the OVM. function _depositEthToWeth() internal { - if (address(this).balance > 0) weth.deposit{ value: address(this).balance }(); + if (address(this).balance > 0) wrappedNativeToken.deposit{ value: address(this).balance }(); } function _bridgeTokensToHubPool(RelayerRefundLeaf memory relayerRefundLeaf) internal override { // If the token being bridged is WETH then we need to first unwrap it to ETH and then send ETH over the // canonical bridge. On Optimism, this is address 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000. - if (relayerRefundLeaf.l2TokenAddress == address(weth)) { + if (relayerRefundLeaf.l2TokenAddress == address(wrappedNativeToken)) { WETH9(relayerRefundLeaf.l2TokenAddress).withdraw(relayerRefundLeaf.amountToReturn); // Unwrap into ETH. relayerRefundLeaf.l2TokenAddress = l2Eth; // Set the l2TokenAddress to ETH. } diff --git a/contracts/Polygon_SpokePool.sol b/contracts/Polygon_SpokePool.sol index 0d98a4132..1ae0db2f8 100644 --- a/contracts/Polygon_SpokePool.sol +++ b/contracts/Polygon_SpokePool.sol @@ -65,8 +65,7 @@ contract Polygon_SpokePool is IFxMessageProcessor, SpokePool { * @param _polygonTokenBridger Token routing contract that sends tokens from here to HubPool. Changeable by Admin. * @param _crossDomainAdmin Cross domain admin to set. Can be changed by admin. * @param _hubPool Hub pool address to set. Can be changed by admin. - * @param _wmaticAddress Replaces _wethAddress for this network since MATIC is the gas token and sent via msg.value - * on Polygon. + * @param _wmaticAddress Replaces wrappedNativeToken for this network since MATIC is the native currency on polygon. * @param _fxChild FxChild contract, changeable by Admin. * @param timerAddress Timer address to set. */ @@ -83,7 +82,7 @@ contract Polygon_SpokePool is IFxMessageProcessor, SpokePool { } /******************************************************** - * ARBITRUM-SPECIFIC CROSS-CHAIN ADMIN FUNCTIONS * + * POLYGON-SPECIFIC CROSS-CHAIN ADMIN FUNCTIONS * ********************************************************/ /** @@ -138,11 +137,11 @@ contract Polygon_SpokePool is IFxMessageProcessor, SpokePool { relayerRefundLeaf.amountToReturn ); - // Note: WETH is WMATIC on matic, so this tells the tokenbridger that this is an unwrappable native token. + // Note: WrappedNativeToken is WMATIC on matic, so this tells the tokenbridger that this is an unwrappable native token. polygonTokenBridger.send( PolygonIERC20(relayerRefundLeaf.l2TokenAddress), relayerRefundLeaf.amountToReturn, - address(weth) == relayerRefundLeaf.l2TokenAddress + address(wrappedNativeToken) == relayerRefundLeaf.l2TokenAddress ); emit PolygonTokensBridged(relayerRefundLeaf.l2TokenAddress, address(this), relayerRefundLeaf.amountToReturn); diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol index 504a8ace8..adab439db 100644 --- a/contracts/SpokePool.sol +++ b/contracts/SpokePool.sol @@ -35,9 +35,9 @@ abstract contract SpokePool is SpokePoolInterface, Testable, Lockable, MultiCall // refunds and slow relays. address public hubPool; - // Address of WETH contract for this network. If an origin token matches this, then the caller can optionally - // instruct this contract to wrap ETH when depositing. - WETH9 public immutable weth; + // Address of wrappedNativeToken contract for this network. If an origin token matches this, then the caller can + // optionally instruct this contract to wrap native tokens when depositing (ie ETH->WETH or MATIC->WMATIC). + WETH9 public immutable wrappedNativeToken; // Any deposit quote times greater than or less than this value to the current contract time is blocked. Forces // caller to use an approximately "current" realized fee. Defaults to 10 minutes. @@ -123,18 +123,18 @@ abstract contract SpokePool is SpokePoolInterface, Testable, Lockable, MultiCall * @notice Construct the base SpokePool. * @param _crossDomainAdmin Cross domain admin to set. Can be changed by admin. * @param _hubPool Hub pool address to set. Can be changed by admin. - * @param _wethAddress Weth address for this network to set. + * @param _wrappedNativeTokenAddress wrappedNativeToken address for this network to set. * @param timerAddress Timer address to set. */ constructor( address _crossDomainAdmin, address _hubPool, - address _wethAddress, + address _wrappedNativeTokenAddress, address timerAddress ) Testable(timerAddress) { _setCrossDomainAdmin(_crossDomainAdmin); _setHubPool(_hubPool); - weth = WETH9(_wethAddress); + wrappedNativeToken = WETH9(_wrappedNativeTokenAddress); } /**************************************** @@ -197,9 +197,9 @@ abstract contract SpokePool is SpokePoolInterface, Testable, Lockable, MultiCall * slow relays, and send funds back to the HubPool on L1. This method can only be called by the admin and is * designed to be called as part of a cross-chain message from the HubPool's executeRootBundle method. * @param relayerRefundRoot Merkle root containing relayer refund leaves that can be individually executed via - * executeRelayerRefundRoot(). + * executeRelayerRefundLeaf(). * @param slowRelayRoot Merkle root containing slow relay fulfillment leaves that can be individually executed via - * executeSlowRelayRoot(). + * executeSlowRelayLeaf(). */ function relayRootBundle(bytes32 relayerRefundRoot, bytes32 slowRelayRoot) public override onlyAdmin nonReentrant { uint32 rootBundleId = uint32(rootBundles.length); @@ -230,8 +230,8 @@ abstract contract SpokePool is SpokePoolInterface, Testable, Lockable, MultiCall * token mapping is stored on the L1 HubPool. * @notice The caller must first approve this contract to spend amount of originToken. * @notice The originToken => destinationChainId must be enabled. - * @notice This method is payable because the caller is able to deposit ETH if the originToken is WETH and this - * function will handle wrapping ETH. + * @notice This method is payable because the caller is able to deposit native token if the originToken is + * wrappedNativeToken and this function will handle wrapping the native token to wrappedNativeToken. * @param recipient Address to receive funds at on destination chain. * @param originToken Token to lock into this contract to initiate deposit. * @param amount Amount of tokens to deposit. Will be amount of tokens to receive less fees. @@ -263,11 +263,11 @@ abstract contract SpokePool is SpokePoolInterface, Testable, Lockable, MultiCall getCurrentTime() <= quoteTimestamp + depositQuoteTimeBuffer, "invalid quote time" ); - // If the address of the origin token is a WETH contract and there is a msg.value with the transaction - // then the user is sending ETH. In this case, the ETH should be deposited to WETH. - if (originToken == address(weth) && msg.value > 0) { + // If the address of the origin token is a wrappedNativeToken contract and there is a msg.value with the + // transaction then the user is sending ETH. In this case, the ETH should be deposited to wrappedNativeToken. + if (originToken == address(wrappedNativeToken) && msg.value > 0) { require(msg.value == amount, "msg.value must match amount"); - weth.deposit{ value: msg.value }(); + wrappedNativeToken.deposit{ value: msg.value }(); // Else, it is a normal ERC20. In this case pull the token from the user's wallet as per normal. // Note: this includes the case where the L2 user has WETH (already wrapped ETH) and wants to bridge them. // In this case the msg.value will be set to 0, indicating a "normal" ERC20 bridging action. @@ -460,7 +460,7 @@ abstract contract SpokePool is SpokePoolInterface, Testable, Lockable, MultiCall * @param rootBundleId Unique ID of root bundle containing slow relay root that this leaf is contained in. * @param proof Inclusion proof for this leaf in slow relay root in root bundle. */ - function executeSlowRelayRoot( + function executeSlowRelayLeaf( address depositor, address recipient, address destinationToken, @@ -472,7 +472,7 @@ abstract contract SpokePool is SpokePoolInterface, Testable, Lockable, MultiCall uint32 rootBundleId, bytes32[] memory proof ) public virtual override nonReentrant { - _executeSlowRelayRoot( + _executeSlowRelayLeaf( depositor, recipient, destinationToken, @@ -495,12 +495,12 @@ abstract contract SpokePool is SpokePoolInterface, Testable, Lockable, MultiCall * refund relayer. This data structure is explained in detail in the SpokePoolInterface. * @param proof Inclusion proof for this leaf in relayer refund root in root bundle. */ - function executeRelayerRefundRoot( + function executeRelayerRefundLeaf( uint32 rootBundleId, SpokePoolInterface.RelayerRefundLeaf memory relayerRefundLeaf, bytes32[] memory proof ) public virtual override nonReentrant { - _executeRelayerRefundRoot(rootBundleId, relayerRefundLeaf, proof); + _executeRelayerRefundLeaf(rootBundleId, relayerRefundLeaf, proof); } /************************************** @@ -521,7 +521,7 @@ abstract contract SpokePool is SpokePoolInterface, Testable, Lockable, MultiCall // Verifies inclusion proof of leaf in root, sends relayer their refund, and sends to HubPool any rebalance // transfers. - function _executeRelayerRefundRoot( + function _executeRelayerRefundLeaf( uint32 rootBundleId, SpokePoolInterface.RelayerRefundLeaf memory relayerRefundLeaf, bytes32[] memory proof @@ -577,7 +577,7 @@ abstract contract SpokePool is SpokePoolInterface, Testable, Lockable, MultiCall } // Verifies inclusion proof of leaf in root and sends recipient remainder of relay. Marks relay as filled. - function _executeSlowRelayRoot( + function _executeSlowRelayLeaf( address depositor, address recipient, address destinationToken, @@ -687,12 +687,12 @@ abstract contract SpokePool is SpokePoolInterface, Testable, Lockable, MultiCall return keccak256(abi.encode(relayData)); } - // Unwraps ETH and does a transfer to a recipient address. If the recipient is a smart contract then sends WETH. - function _unwrapWETHTo(address payable to, uint256 amount) internal { + // Unwraps ETH and does a transfer to a recipient address. If the recipient is a smart contract then sends wrappedNativeToken. + function _unwrapwrappedNativeTokenTo(address payable to, uint256 amount) internal { if (address(to).isContract()) { - IERC20(address(weth)).safeTransfer(to, amount); + IERC20(address(wrappedNativeToken)).safeTransfer(to, amount); } else { - weth.withdraw(amount); + wrappedNativeToken.withdraw(amount); to.transfer(amount); } } @@ -755,15 +755,16 @@ abstract contract SpokePool is SpokePoolInterface, Testable, Lockable, MultiCall // fill any relay up to 100 tokens, and partial fill with 100 tokens for larger relays). relayFills[relayHash] += fillAmountPreFees; - // If relay token is weth then unwrap and send eth. - if (relayData.destinationToken == address(weth)) { + // If relay token is wrappedNativeToken then unwrap and send native token. + if (relayData.destinationToken == address(wrappedNativeToken)) { // Note: useContractFunds is True if we want to send funds to the recipient directly out of this contract, // otherwise we expect the caller to send funds to the recipient. If useContractFunds is True and the - // recipient wants WETH, then we can assume that WETH is already in the contract, otherwise we'll need the - // the user to send WETH to this contract. Regardless, we'll need to unwrap it before sending to the user. + // recipient wants wrappedNativeToken, then we can assume that wrappedNativeToken is already in the + // contract, otherwise we'll need the user to send wrappedNativeToken to this contract. Regardless, we'll + // need to unwrap it to native token before sending to the user. if (!useContractFunds) IERC20(relayData.destinationToken).safeTransferFrom(msg.sender, address(this), amountToSend); - _unwrapWETHTo(payable(relayData.recipient), amountToSend); + _unwrapwrappedNativeTokenTo(payable(relayData.recipient), amountToSend); // Else, this is a normal ERC20 token. Send to recipient. } else { // Note: Similar to note above, send token directly from the contract to the user in the slow relay case. @@ -830,6 +831,6 @@ abstract contract SpokePool is SpokePoolInterface, Testable, Lockable, MultiCall // L1, this would just be the same admin of the HubPool. function _requireAdminSender() internal virtual; - // Added to enable the this contract to receive ETH. Used when unwrapping Weth. + // Added to enable the this contract to receive native token (ETH). Used when unwrapping wrappedNativeToken. receive() external payable {} } diff --git a/contracts/SpokePoolInterface.sol b/contracts/SpokePoolInterface.sol index 3b33f6bbf..d77534d05 100644 --- a/contracts/SpokePoolInterface.sol +++ b/contracts/SpokePoolInterface.sol @@ -120,7 +120,7 @@ interface SpokePoolInterface { bytes memory depositorSignature ) external; - function executeSlowRelayRoot( + function executeSlowRelayLeaf( address depositor, address recipient, address destinationToken, @@ -133,7 +133,7 @@ interface SpokePoolInterface { bytes32[] memory proof ) external; - function executeRelayerRefundRoot( + function executeRelayerRefundLeaf( uint32 rootBundleId, SpokePoolInterface.RelayerRefundLeaf memory relayerRefundLeaf, bytes32[] memory proof diff --git a/test/HubPool.DisputeRootBundle.ts b/test/HubPool.DisputeRootBundle.ts index ce5f1c6dc..2c32e2dec 100644 --- a/test/HubPool.DisputeRootBundle.ts +++ b/test/HubPool.DisputeRootBundle.ts @@ -38,7 +38,7 @@ describe("HubPool Root Bundle Dispute", function () { // Data should be deleted from the contracts refundRequest struct. const rootBundle = await hubPool.rootBundleProposal(); - expect(rootBundle.requestExpirationTimestamp).to.equal(0); + expect(rootBundle.challengePeriodEndTimestamp).to.equal(0); expect(rootBundle.unclaimedPoolRebalanceLeafCount).to.equal(0); expect(rootBundle.poolRebalanceRoot).to.equal(consts.zeroBytes32); expect(rootBundle.relayerRefundRoot).to.equal(consts.zeroBytes32); @@ -97,7 +97,7 @@ describe("HubPool Root Bundle Dispute", function () { // Data should be deleted from the contracts refundRequest struct. const rootBundle = await hubPool.rootBundleProposal(); - expect(rootBundle.requestExpirationTimestamp).to.equal(0); + expect(rootBundle.challengePeriodEndTimestamp).to.equal(0); expect(rootBundle.unclaimedPoolRebalanceLeafCount).to.equal(0); expect(rootBundle.poolRebalanceRoot).to.equal(consts.zeroBytes32); expect(rootBundle.relayerRefundRoot).to.equal(consts.zeroBytes32); @@ -140,7 +140,7 @@ describe("HubPool Root Bundle Dispute", function () { // Data should be deleted from the contracts refundRequest struct. const rootBundle = await hubPool.rootBundleProposal(); - expect(rootBundle.requestExpirationTimestamp).to.equal(0); + expect(rootBundle.challengePeriodEndTimestamp).to.equal(0); expect(rootBundle.unclaimedPoolRebalanceLeafCount).to.equal(0); expect(rootBundle.poolRebalanceRoot).to.equal(consts.zeroBytes32); expect(rootBundle.relayerRefundRoot).to.equal(consts.zeroBytes32); diff --git a/test/HubPool.ProposeRootBundle.ts b/test/HubPool.ProposeRootBundle.ts index 3d7aaf4a9..fa6b3b254 100644 --- a/test/HubPool.ProposeRootBundle.ts +++ b/test/HubPool.ProposeRootBundle.ts @@ -12,7 +12,7 @@ describe("HubPool Root Bundle Proposal", function () { }); it("Proposal of root bundle correctly stores data, emits events and pulls the bond", async function () { - const expectedRequestExpirationTimestamp = Number(await hubPool.getCurrentTime()) + consts.refundProposalLiveness; + const expectedchallengePeriodEndTimestamp = Number(await hubPool.getCurrentTime()) + consts.refundProposalLiveness; await weth.connect(dataWorker).approve(hubPool.address, consts.totalBond); const dataWorkerWethBalancerBefore = await weth.callStatic.balanceOf(dataWorker.address); @@ -29,7 +29,7 @@ describe("HubPool Root Bundle Proposal", function () { ) .to.emit(hubPool, "ProposeRootBundle") .withArgs( - expectedRequestExpirationTimestamp, + expectedchallengePeriodEndTimestamp, consts.mockPoolRebalanceLeafCount, consts.mockBundleEvaluationBlockNumbers, consts.mockPoolRebalanceRoot, @@ -42,7 +42,7 @@ describe("HubPool Root Bundle Proposal", function () { expect(await weth.balanceOf(dataWorker.address)).to.equal(dataWorkerWethBalancerBefore.sub(consts.totalBond)); const rootBundle = await hubPool.rootBundleProposal(); - expect(rootBundle.requestExpirationTimestamp).to.equal(expectedRequestExpirationTimestamp); + expect(rootBundle.challengePeriodEndTimestamp).to.equal(expectedchallengePeriodEndTimestamp); expect(rootBundle.unclaimedPoolRebalanceLeafCount).to.equal(consts.mockPoolRebalanceLeafCount); expect(rootBundle.poolRebalanceRoot).to.equal(consts.mockPoolRebalanceRoot); expect(rootBundle.relayerRefundRoot).to.equal(consts.mockRelayerRefundRoot); diff --git a/test/SpokePool.ExecuteRootBundle.ts b/test/SpokePool.ExecuteRootBundle.ts index 8f336e96d..e883a90e2 100644 --- a/test/SpokePool.ExecuteRootBundle.ts +++ b/test/SpokePool.ExecuteRootBundle.ts @@ -43,7 +43,7 @@ describe("SpokePool Root Bundle Execution", function () { ); // Distribute the first leaf. - await spokePool.connect(dataWorker).executeRelayerRefundRoot(0, leaves[0], tree.getHexProof(leaves[0])); + await spokePool.connect(dataWorker).executeRelayerRefundLeaf(0, leaves[0], tree.getHexProof(leaves[0])); // Relayers should be refunded expect(await destErc20.balanceOf(spokePool.address)).to.equal(consts.amountHeldByPool.sub(leavesRefundAmount)); @@ -65,7 +65,8 @@ describe("SpokePool Root Bundle Execution", function () { expect(tokensBridgedEvents.length).to.equal(1); // Does not attempt to bridge tokens if amountToReturn is 0. Execute a leaf where amountToReturn is 0. - await spokePool.connect(dataWorker).executeRelayerRefundRoot(0, leaves[1], tree.getHexProof(leaves[1])); + await spokePool.connect(dataWorker).executeRelayerRefundLeaf(0, leaves[1], tree.getHexProof(leaves[1])); + // Show that a second DistributedRelayRefund event was emitted but not a second TokensBridged event. relayTokensEvents = await spokePool.queryFilter(spokePool.filters.ExecutedRelayerRefundRoot()); expect(relayTokensEvents.length).to.equal(2); @@ -83,11 +84,11 @@ describe("SpokePool Root Bundle Execution", function () { // Take the valid root but change some element within it. This will change the hash of the leaf // and as such the contract should reject it for not being included within the merkle tree for the valid proof. const badLeaf = { ...leaves[0], chainId: 13371 }; - await expect(spokePool.connect(dataWorker).executeRelayerRefundRoot(0, badLeaf, tree.getHexProof(leaves[0]))).to.be + await expect(spokePool.connect(dataWorker).executeRelayerRefundLeaf(0, badLeaf, tree.getHexProof(leaves[0]))).to.be .reverted; // Reverts if the distribution root index is incorrect. - await expect(spokePool.connect(dataWorker).executeRelayerRefundRoot(1, leaves[0], tree.getHexProof(leaves[0]))).to + await expect(spokePool.connect(dataWorker).executeRelayerRefundLeaf(1, leaves[0], tree.getHexProof(leaves[0]))).to .be.reverted; }); it("Cannot refund leaf with chain ID for another network", async function () { @@ -99,7 +100,7 @@ describe("SpokePool Root Bundle Execution", function () { ); // Root is valid and leaf is contained in tree, but chain ID doesn't match pool's chain ID. - await expect(spokePool.connect(dataWorker).executeRelayerRefundRoot(0, leaves[0], tree.getHexProof(leaves[0]))).to + await expect(spokePool.connect(dataWorker).executeRelayerRefundLeaf(0, leaves[0], tree.getHexProof(leaves[0]))).to .be.reverted; }); it("Execution rejects double claimed leaves", async function () { @@ -110,8 +111,8 @@ describe("SpokePool Root Bundle Execution", function () { ); // First claim should be fine. Second claim should be reverted as you cant double claim a leaf. - await spokePool.connect(dataWorker).executeRelayerRefundRoot(0, leaves[0], tree.getHexProof(leaves[0])); - await expect(spokePool.connect(dataWorker).executeRelayerRefundRoot(0, leaves[0], tree.getHexProof(leaves[0]))).to + await spokePool.connect(dataWorker).executeRelayerRefundLeaf(0, leaves[0], tree.getHexProof(leaves[0])); + await expect(spokePool.connect(dataWorker).executeRelayerRefundLeaf(0, leaves[0], tree.getHexProof(leaves[0]))).to .be.reverted; }); }); diff --git a/test/SpokePool.SlowRelay.ts b/test/SpokePool.SlowRelay.ts index c749059e2..b4dd8ff0d 100644 --- a/test/SpokePool.SlowRelay.ts +++ b/test/SpokePool.SlowRelay.ts @@ -90,7 +90,7 @@ describe("SpokePool Slow Relay Logic", async function () { await expect(() => spokePool .connect(relayer) - .executeSlowRelayRoot( + .executeSlowRelayLeaf( ...getExecuteSlowRelayParams( depositor.address, recipient.address, @@ -117,7 +117,7 @@ describe("SpokePool Slow Relay Logic", async function () { await expect( spokePool .connect(relayer) - .executeSlowRelayRoot( + .executeSlowRelayLeaf( ...getExecuteSlowRelayParams( depositor.address, recipient.address, @@ -156,7 +156,7 @@ describe("SpokePool Slow Relay Logic", async function () { await expect(() => spokePool .connect(relayer) - .executeSlowRelayRoot( + .executeSlowRelayLeaf( ...getExecuteSlowRelayParams( depositor.address, recipient.address, @@ -177,7 +177,7 @@ describe("SpokePool Slow Relay Logic", async function () { await expect(() => spokePool .connect(relayer) - .executeSlowRelayRoot( + .executeSlowRelayLeaf( ...getExecuteSlowRelayParams( depositor.address, recipient.address, @@ -221,7 +221,7 @@ describe("SpokePool Slow Relay Logic", async function () { await expect(() => spokePool .connect(relayer) - .executeSlowRelayRoot( + .executeSlowRelayLeaf( ...getExecuteSlowRelayParams( depositor.address, recipient.address, @@ -264,7 +264,7 @@ describe("SpokePool Slow Relay Logic", async function () { await expect(() => spokePool .connect(relayer) - .executeSlowRelayRoot( + .executeSlowRelayLeaf( ...getExecuteSlowRelayParams( depositor.address, recipient.address, @@ -307,7 +307,7 @@ describe("SpokePool Slow Relay Logic", async function () { await expect(() => spokePool .connect(relayer) - .executeSlowRelayRoot( + .executeSlowRelayLeaf( ...getExecuteSlowRelayParams( depositor.address, recipient.address, @@ -333,7 +333,7 @@ describe("SpokePool Slow Relay Logic", async function () { await expect( spokePool .connect(relayer) - .executeSlowRelayRoot( + .executeSlowRelayLeaf( ...getExecuteSlowRelayParams( relay.depositor, relay.recipient, @@ -352,7 +352,7 @@ describe("SpokePool Slow Relay Logic", async function () { it("Bad proof: Relay data besides destination chain ID is not included in merkle root", async function () { await expect( - spokePool.connect(relayer).executeSlowRelayRoot( + spokePool.connect(relayer).executeSlowRelayLeaf( ...getExecuteSlowRelayParams( depositor.address, recipient.address, diff --git a/test/chain-specific-spokepools/Arbitrum_SpokePool.ts b/test/chain-specific-spokepools/Arbitrum_SpokePool.ts index 25b5af048..1bc333008 100644 --- a/test/chain-specific-spokepools/Arbitrum_SpokePool.ts +++ b/test/chain-specific-spokepools/Arbitrum_SpokePool.ts @@ -93,11 +93,11 @@ describe("Arbitrum Spoke Pool", function () { // Reverts if route from arbitrum to mainnet for l2Dai isn't whitelisted. await arbitrumSpokePool.connect(crossDomainAlias).whitelistToken(l2Dai, ZERO_ADDRESS); await expect( - arbitrumSpokePool.executeRelayerRefundRoot(0, leaves[0], tree.getHexProof(leaves[0])) + arbitrumSpokePool.executeRelayerRefundLeaf(0, leaves[0], tree.getHexProof(leaves[0])) ).to.be.revertedWith("Uninitialized mainnet token"); await arbitrumSpokePool.connect(crossDomainAlias).whitelistToken(l2Dai, dai.address); - await arbitrumSpokePool.connect(relayer).executeRelayerRefundRoot(0, leaves[0], tree.getHexProof(leaves[0])); + await arbitrumSpokePool.connect(relayer).executeRelayerRefundLeaf(0, leaves[0], tree.getHexProof(leaves[0])); // This should have sent tokens back to L1. Check the correct methods on the gateway are correctly called. // outboundTransfer is overloaded in the arbitrum gateway. Define the interface to check the method is called. diff --git a/test/chain-specific-spokepools/Ethereum_SpokePool.ts b/test/chain-specific-spokepools/Ethereum_SpokePool.ts index 6d4b8c8d0..cd194b0e4 100644 --- a/test/chain-specific-spokepools/Ethereum_SpokePool.ts +++ b/test/chain-specific-spokepools/Ethereum_SpokePool.ts @@ -65,7 +65,7 @@ describe("Ethereum Spoke Pool", function () { const { leaves, tree } = await constructSingleRelayerRefundTree(dai.address, await spokePool.callStatic.chainId()); await spokePool.connect(owner).relayRootBundle(tree.getHexRoot(), mockTreeRoot); await expect(() => - spokePool.connect(relayer).executeRelayerRefundRoot(0, leaves[0], tree.getHexProof(leaves[0])) + spokePool.connect(relayer).executeRelayerRefundLeaf(0, leaves[0], tree.getHexProof(leaves[0])) ).to.changeTokenBalances(dai, [spokePool, hubPool], [amountToReturn.mul(-1), amountToReturn]); }); }); diff --git a/test/chain-specific-spokepools/Optimism_SpokePool.ts b/test/chain-specific-spokepools/Optimism_SpokePool.ts index 3c1195e38..abd4eac2e 100644 --- a/test/chain-specific-spokepools/Optimism_SpokePool.ts +++ b/test/chain-specific-spokepools/Optimism_SpokePool.ts @@ -102,7 +102,7 @@ describe("Optimism Spoke Pool", function () { ); crossDomainMessenger.xDomainMessageSender.returns(owner.address); await optimismSpokePool.connect(crossDomainMessenger.wallet).relayRootBundle(tree.getHexRoot(), mockTreeRoot); - await optimismSpokePool.connect(relayer).executeRelayerRefundRoot(0, leaves[0], tree.getHexProof(leaves[0])); + await optimismSpokePool.connect(relayer).executeRelayerRefundLeaf(0, leaves[0], tree.getHexProof(leaves[0])); // This should have sent tokens back to L1. Check the correct methods on the gateway are correctly called. expect(l2StandardBridge.withdrawTo).to.have.been.calledOnce; @@ -117,7 +117,7 @@ describe("Optimism Spoke Pool", function () { await optimismSpokePool.connect(crossDomainMessenger.wallet).relayRootBundle(tree.getHexRoot(), mockTreeRoot); const altL2Bridge = await createFake("L2StandardBridge"); await optimismSpokePool.connect(crossDomainMessenger.wallet).setTokenBridge(l2Dai, altL2Bridge.address); - await optimismSpokePool.connect(relayer).executeRelayerRefundRoot(0, leaves[0], tree.getHexProof(leaves[0])); + await optimismSpokePool.connect(relayer).executeRelayerRefundLeaf(0, leaves[0], tree.getHexProof(leaves[0])); // This should have sent tokens back to L1. Check the correct methods on the gateway are correctly called. expect(altL2Bridge.withdrawTo).to.have.been.calledOnce; @@ -130,7 +130,7 @@ describe("Optimism Spoke Pool", function () { ); crossDomainMessenger.xDomainMessageSender.returns(owner.address); await optimismSpokePool.connect(crossDomainMessenger.wallet).relayRootBundle(tree.getHexRoot(), mockTreeRoot); - await optimismSpokePool.connect(relayer).executeRelayerRefundRoot(0, leaves[0], tree.getHexProof(leaves[0])); + await optimismSpokePool.connect(relayer).executeRelayerRefundLeaf(0, leaves[0], tree.getHexProof(leaves[0])); // When sending l2Weth we should see two differences from the previous test: 1) there should be a call to l2WETH to // unwrap l2WETH to l2ETH. 2) the address in the l2StandardBridge that is withdrawn should no longer be l2WETH but diff --git a/test/chain-specific-spokepools/Polygon_SpokePool.ts b/test/chain-specific-spokepools/Polygon_SpokePool.ts index 56a75a4fd..ad4d0c69d 100644 --- a/test/chain-specific-spokepools/Polygon_SpokePool.ts +++ b/test/chain-specific-spokepools/Polygon_SpokePool.ts @@ -190,7 +190,7 @@ describe("Polygon Spoke Pool", function () { const bridger = await polygonSpokePool.polygonTokenBridger(); // Checks that there's a burn event from the bridger. - await expect(polygonSpokePool.connect(relayer).executeRelayerRefundRoot(0, leaves[0], tree.getHexProof(leaves[0]))) + await expect(polygonSpokePool.connect(relayer).executeRelayerRefundLeaf(0, leaves[0], tree.getHexProof(leaves[0]))) .to.emit(dai, "Transfer") .withArgs(bridger, ZERO_ADDRESS, amountToReturn); }); diff --git a/test/gas-analytics/SpokePool.RelayerRefundRootExecution.ts b/test/gas-analytics/SpokePool.RelayerRefundRootExecution.ts index 5419a0b02..abdf9ead6 100644 --- a/test/gas-analytics/SpokePool.RelayerRefundRootExecution.ts +++ b/test/gas-analytics/SpokePool.RelayerRefundRootExecution.ts @@ -150,7 +150,7 @@ describe("Gas Analytics: SpokePool Relayer Refund Root Execution", function () { // Execute 1 leaf from initial tree to warm state storage. await spokePool .connect(dataWorker) - .executeRelayerRefundRoot(0, initTree.leaves[0], initTree.tree.getHexProof(initTree.leaves[0])); + .executeRelayerRefundLeaf(0, initTree.leaves[0], initTree.tree.getHexProof(initTree.leaves[0])); const simpleTree = await constructSimpleTree( destinationChainIds, @@ -176,10 +176,10 @@ describe("Gas Analytics: SpokePool Relayer Refund Root Execution", function () { // Execute second root bundle with index 1: const txn = await spokePool .connect(dataWorker) - .executeRelayerRefundRoot(1, leaves[leafIndexToExecute], tree.getHexProof(leaves[leafIndexToExecute])); + .executeRelayerRefundLeaf(1, leaves[leafIndexToExecute], tree.getHexProof(leaves[leafIndexToExecute])); const receipt = await txn.wait(); - console.log(`executeRelayerRefundRoot-gasUsed: ${receipt.gasUsed}`); + console.log(`executeRelayerRefundLeaf-gasUsed: ${receipt.gasUsed}`); }); it("Executing all leaves", async function () { await spokePool.connect(dataWorker).relayRootBundle(tree.getHexRoot(), consts.mockSlowRelayRoot); @@ -187,25 +187,25 @@ describe("Gas Analytics: SpokePool Relayer Refund Root Execution", function () { const txns = []; for (let i = 0; i < REFUND_LEAF_COUNT; i++) { txns.push( - await spokePool.connect(dataWorker).executeRelayerRefundRoot(1, leaves[i], tree.getHexProof(leaves[i])) + await spokePool.connect(dataWorker).executeRelayerRefundLeaf(1, leaves[i], tree.getHexProof(leaves[i])) ); } // Compute average gas costs. const receipts = await Promise.all(txns.map((_txn) => _txn.wait())); const gasUsed = receipts.map((_receipt) => _receipt.gasUsed).reduce((x, y) => x.add(y)); - console.log(`(average) executeRelayerRefundRoot-gasUsed: ${gasUsed.div(REFUND_LEAF_COUNT)}`); + console.log(`(average) executeRelayerRefundLeaf-gasUsed: ${gasUsed.div(REFUND_LEAF_COUNT)}`); }); it("Executing all leaves using multicall", async function () { await spokePool.connect(dataWorker).relayRootBundle(tree.getHexRoot(), consts.mockSlowRelayRoot); const multicallData = leaves.map((leaf) => { - return spokePool.interface.encodeFunctionData("executeRelayerRefundRoot", [1, leaf, tree.getHexProof(leaf)]); + return spokePool.interface.encodeFunctionData("executeRelayerRefundLeaf", [1, leaf, tree.getHexProof(leaf)]); }); const receipt = await (await spokePool.connect(dataWorker).multicall(multicallData)).wait(); - console.log(`(average) executeRelayerRefundRoot-gasUsed: ${receipt.gasUsed.div(REFUND_LEAF_COUNT)}`); + console.log(`(average) executeRelayerRefundLeaf-gasUsed: ${receipt.gasUsed.div(REFUND_LEAF_COUNT)}`); }); }); describe(`(WETH): Relayer Refund tree with ${REFUND_LEAF_COUNT} leaves, each containing ${REFUNDS_PER_LEAF} refunds`, function () { @@ -228,7 +228,7 @@ describe("Gas Analytics: SpokePool Relayer Refund Root Execution", function () { // Execute 1 leaf from initial tree to warm state storage. await spokePool .connect(dataWorker) - .executeRelayerRefundRoot(0, initTree.leaves[0], initTree.tree.getHexProof(initTree.leaves[0])); + .executeRelayerRefundLeaf(0, initTree.leaves[0], initTree.tree.getHexProof(initTree.leaves[0])); const simpleTree = await constructSimpleTree( destinationChainIds, @@ -248,10 +248,10 @@ describe("Gas Analytics: SpokePool Relayer Refund Root Execution", function () { // Execute second root bundle with index 1: const txn = await spokePool .connect(dataWorker) - .executeRelayerRefundRoot(1, leaves[leafIndexToExecute], tree.getHexProof(leaves[leafIndexToExecute])); + .executeRelayerRefundLeaf(1, leaves[leafIndexToExecute], tree.getHexProof(leaves[leafIndexToExecute])); const receipt = await txn.wait(); - console.log(`executeRelayerRefundRoot-gasUsed: ${receipt.gasUsed}`); + console.log(`executeRelayerRefundLeaf-gasUsed: ${receipt.gasUsed}`); }); it(`Stress Test: 1 leaf contains ${STRESS_TEST_REFUND_COUNT} refunds with amount > 0`, async function () { // This test should inform the limit # refunds that we would allow a RelayerRefundLeaf to contain to avoid diff --git a/test/gas-analytics/SpokePool.SlowRelayRootExecution.ts b/test/gas-analytics/SpokePool.SlowRelayRootExecution.ts index 48693a782..664b353bf 100644 --- a/test/gas-analytics/SpokePool.SlowRelayRootExecution.ts +++ b/test/gas-analytics/SpokePool.SlowRelayRootExecution.ts @@ -97,7 +97,7 @@ describe("Gas Analytics: SpokePool Slow Relay Root Execution", function () { // Execute 1 leaf from initial tree to warm state storage. await spokePool .connect(dataWorker) - .executeSlowRelayRoot( + .executeSlowRelayLeaf( owner.address, recipient.address, l2Tokens[0].address, @@ -133,7 +133,7 @@ describe("Gas Analytics: SpokePool Slow Relay Root Execution", function () { // Execute second root bundle with index 1: const txn = await spokePool .connect(dataWorker) - .executeSlowRelayRoot( + .executeSlowRelayLeaf( owner.address, recipient.address, l2Tokens[0].address, @@ -146,7 +146,7 @@ describe("Gas Analytics: SpokePool Slow Relay Root Execution", function () { tree.getHexProof(leaves[leafIndexToExecute]) ); const receipt = await txn.wait(); - console.log(`executeSlowRelayRoot-gasUsed: ${receipt.gasUsed}`); + console.log(`executeSlowRelayLeaf-gasUsed: ${receipt.gasUsed}`); }); it("Executing all leaves", async function () { await spokePool.connect(dataWorker).relayRootBundle(consts.mockRelayerRefundRoot, tree.getHexRoot()); @@ -156,7 +156,7 @@ describe("Gas Analytics: SpokePool Slow Relay Root Execution", function () { txns.push( await spokePool .connect(dataWorker) - .executeSlowRelayRoot( + .executeSlowRelayLeaf( owner.address, recipient.address, l2Tokens[i].address, @@ -174,14 +174,14 @@ describe("Gas Analytics: SpokePool Slow Relay Root Execution", function () { // Compute average gas costs. const receipts = await Promise.all(txns.map((_txn) => _txn.wait())); const gasUsed = receipts.map((_receipt) => _receipt.gasUsed).reduce((x, y) => x.add(y)); - console.log(`(average) executeSlowRelayRoot-gasUsed: ${gasUsed.div(LEAF_COUNT)}`); + console.log(`(average) executeSlowRelayLeaf-gasUsed: ${gasUsed.div(LEAF_COUNT)}`); }); it("Executing all leaves using multicall", async function () { await spokePool.connect(dataWorker).relayRootBundle(consts.mockRelayerRefundRoot, tree.getHexRoot()); const multicallData = leaves.map((leaf, i) => { - return spokePool.interface.encodeFunctionData("executeSlowRelayRoot", [ + return spokePool.interface.encodeFunctionData("executeSlowRelayLeaf", [ owner.address, recipient.address, l2Tokens[i].address, @@ -196,7 +196,7 @@ describe("Gas Analytics: SpokePool Slow Relay Root Execution", function () { }); const receipt = await (await spokePool.connect(dataWorker).multicall(multicallData)).wait(); - console.log(`(average) executeSlowRelayRoot-gasUsed: ${receipt.gasUsed.div(LEAF_COUNT)}`); + console.log(`(average) executeSlowRelayLeaf-gasUsed: ${receipt.gasUsed.div(LEAF_COUNT)}`); }); }); describe(`(WETH) Tree with ${LEAF_COUNT} leaves`, function () { @@ -215,7 +215,7 @@ describe("Gas Analytics: SpokePool Slow Relay Root Execution", function () { // Execute 1 leaf from initial tree to warm state storage. await spokePool .connect(dataWorker) - .executeSlowRelayRoot( + .executeSlowRelayLeaf( owner.address, recipient.address, weth.address, @@ -246,7 +246,7 @@ describe("Gas Analytics: SpokePool Slow Relay Root Execution", function () { // Execute second root bundle with index 1: const txn = await spokePool .connect(dataWorker) - .executeSlowRelayRoot( + .executeSlowRelayLeaf( owner.address, recipient.address, weth.address, @@ -259,7 +259,7 @@ describe("Gas Analytics: SpokePool Slow Relay Root Execution", function () { tree.getHexProof(leaves[leafIndexToExecute]) ); const receipt = await txn.wait(); - console.log(`executeSlowRelayRoot-gasUsed: ${receipt.gasUsed}`); + console.log(`executeSlowRelayLeaf-gasUsed: ${receipt.gasUsed}`); }); }); });