-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[INTAUTO-272] Functions support zksync (#15991)
* fix: point to 1.3.0 ocr * chore: add functions router zksync to solhint ignore * chore: add changeset * chore: removed not modified contracts * fix: remove l1 fee logic * fix: unused vars * Adds exact gas bound call implementation for zkSync This commit adds a safe implementation for the gas bound call implementation for zksync. It also adds a custom Functions router contract for zkSync. * Bump solidity version * Typo fix * Minor changes * Adds solidity version 0.8.20 * Addressing comments * Address feedback * Address feedback * Typo fix * Replaced require statements with custom error and other solhint recomendations * Downgrade shared solidity version to 0.8.20 * Set evm version to paris to fix tests * Fix comment * Auto detect shared solc version for shared * Uses rawCall and vendors zksync dependency * Adds tests and used near call pattern * Downgrade hardhat * Downgrade to solidity 0.8.19 * Adds zkSyncFunctionsRouter tests * Adds gas snapshots * Adds gas snapshot for shared * Solhint fixes * fix: call with exact gas * Adds more tests and addresses comments * Fixes comment and adjusts test * Updates gas snapshot * Adds pnpm lock file * Modifies comment * Modifies comment * Addresses comments * Updates gas snapshot * build CL image also on keystone changes --------- Co-authored-by: Kodey Kilday-Thomas <[email protected]> Co-authored-by: Bartek Tofel <[email protected]> Co-authored-by: Bartek Tofel <[email protected]>
- Loading branch information
1 parent
95fa633
commit ff814eb
Showing
19 changed files
with
1,507 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@chainlink/contracts': patch | ||
--- | ||
|
||
Added ZKSync support for Functions |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
195 changes: 195 additions & 0 deletions
195
contracts/src/v0.8/functions/tests/v1_X/ZKSyncFunctionsRouter.t.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.19; | ||
|
||
import {BaseTest} from "./BaseTest.t.sol"; | ||
import {ZKSyncFunctionsRouter} from "../../v1_3_0_zksync/ZKSyncFunctionsRouter.sol"; | ||
import {FunctionsRouter} from "../../v1_0_0/FunctionsRouter.sol"; | ||
import {ZKSyncFunctionsRouterHarness} from "./testhelpers/ZKSyncFunctionsRouterHarness.sol"; | ||
import {ZKSyncFunctionsRouterSetup} from "./Setup.t.sol"; | ||
import {MockSystemContext} from "../../../shared/test/mocks/MockSystemContext.sol"; | ||
|
||
contract ZKSyncFunctionsRouter__Callback is ZKSyncFunctionsRouterSetup { | ||
MockClientSuccess internal s_mockClientSuccess; | ||
MockClientRevert internal s_mockClientRevert; | ||
MockSystemContext internal s_mockSystemContext; | ||
|
||
struct CallbackResult { | ||
bool success; | ||
uint256 gasUsed; | ||
bytes returnData; | ||
} | ||
|
||
function setUp() public virtual override { | ||
super.setUp(); | ||
s_mockClientSuccess = new MockClientSuccess(); | ||
s_mockClientRevert = new MockClientRevert(); | ||
|
||
s_mockSystemContext = new MockSystemContext(); | ||
// Write mock's code to 0x800b so that library calls see it | ||
vm.etch(address(0x800b), address(s_mockSystemContext).code); | ||
} | ||
|
||
function test__callback_RevertWhen_NoClientCode() public { | ||
bytes32 reqId = bytes32("reqIdNoCode"); | ||
bytes memory resp = bytes("responseData"); | ||
bytes memory err = bytes("errData"); | ||
uint32 totalGas = 5_000_000; | ||
uint32 callbackGasLimit = 4_000_000; | ||
address noCodeAddress = address(12345); | ||
|
||
ZKSyncFunctionsRouter.CallbackResult memory result = _callback( | ||
reqId, | ||
resp, | ||
err, | ||
totalGas, | ||
callbackGasLimit, | ||
noCodeAddress | ||
); | ||
|
||
assertFalse(result.success, "Should skip => success=false"); | ||
assertEq(result.gasUsed, 0, "gasUsed=0 for skip"); | ||
assertEq(result.returnData.length, 0, "no return data"); | ||
} | ||
|
||
function test__callback_Success() public { | ||
bytes32 reqId = bytes32("reqSuccess"); | ||
bytes memory resp = bytes("responseData"); | ||
bytes memory err = bytes("errData"); | ||
uint32 totalGas = 5_000_000; | ||
uint32 callbackGasLimit = 4_000_000; | ||
address client = address(s_mockClientSuccess); | ||
|
||
ZKSyncFunctionsRouter.CallbackResult memory result = _callback( | ||
reqId, | ||
resp, | ||
err, | ||
totalGas, | ||
callbackGasLimit, | ||
client | ||
); | ||
|
||
assertTrue(result.success, "callback should succeed"); | ||
assertGt(result.gasUsed, 0, "some gas used"); | ||
assertTrue(result.returnData.length > 0, "client returns a bool => should have data"); | ||
} | ||
|
||
function test__callback_RevertWhen_ClientReverts() public { | ||
bytes32 reqId = bytes32("reqIdRevert"); | ||
bytes memory resp = bytes("someResponse"); | ||
bytes memory err = bytes("someErr"); | ||
// Use a moderate gas limit so that we don't trigger the _maxTotalGas check. | ||
uint32 totalGas = 5_000_000; | ||
uint32 callbackGasLimit = 4_000_000; | ||
address client = address(s_mockClientRevert); | ||
ZKSyncFunctionsRouter.CallbackResult memory result = _callback( | ||
reqId, | ||
resp, | ||
err, | ||
totalGas, | ||
callbackGasLimit, | ||
client | ||
); | ||
|
||
assertFalse(result.success, "client revert => success=false"); | ||
assertGt(result.gasUsed, 0, "some gas is consumed"); | ||
// returnData should contain the revert reason "MockClientRevert" | ||
assertTrue(result.returnData.length > 0, "contains revert reason data"); | ||
} | ||
|
||
/// @notice Example test verifying pubdata usage is zero and comparing internal measurement with external gas usage. | ||
function test__callback_PubdataUsage_IsZero() public { | ||
s_mockSystemContext.setGasPerPubdataByte(0); | ||
bytes32 reqId = bytes32("reqPubdata"); | ||
bytes memory resp = bytes("someResponse"); | ||
bytes memory err = bytes("someErr"); | ||
uint32 totalGas = 5_000_000; | ||
uint32 callbackGasLimit = 4_000_000; | ||
address client = address(s_mockClientSuccess); | ||
uint256 startGas = gasleft(); | ||
ZKSyncFunctionsRouter.CallbackResult memory result = _callback( | ||
reqId, | ||
resp, | ||
err, | ||
totalGas, | ||
callbackGasLimit, | ||
client | ||
); | ||
uint256 endGas = gasleft(); | ||
uint256 actualUsed = startGas - endGas; | ||
assertTrue(result.success, "callback success"); | ||
assertGt(result.gasUsed, 0, "callback claims >0 gas used"); | ||
// Allow a margin between the router's internal measurement and actual external usage. | ||
assertLe(result.gasUsed, actualUsed, "Router's gasUsed should not exceed actual external usage by large margin"); | ||
} | ||
|
||
/// @notice Confirm large return data gets truncated by _maxReturnBytes. | ||
function test__callback_ReturnDataTruncation() public { | ||
// Deploy large-return client. | ||
MockClientLargeReturn bigClient = new MockClientLargeReturn(); | ||
|
||
bytes32 reqId = bytes32("reqLargeReturn"); | ||
bytes memory resp = bytes("someResponse"); | ||
bytes memory err = bytes("someErr"); | ||
uint32 totalGas = 5_000_000; | ||
uint32 callbackGasLimit = 4_000_000; | ||
address client = address(bigClient); | ||
|
||
ZKSyncFunctionsRouter.CallbackResult memory result = _callback( | ||
reqId, | ||
resp, | ||
err, | ||
totalGas, | ||
callbackGasLimit, | ||
client | ||
); | ||
assertTrue(result.success, "Should succeed"); | ||
uint256 expectedMax = s_functionsRouter.MAX_CALLBACK_RETURN_BYTES(); | ||
// The returned data should be truncated exactly to expectedMax. | ||
assertEq(result.returnData.length, expectedMax, "Should truncate data to MAX_CALLBACK_RETURN_BYTES"); | ||
} | ||
|
||
/// @notice Internal helper to call the router's exposed callback function. | ||
function _callback( | ||
bytes32 reqId, | ||
bytes memory resp, | ||
bytes memory err, | ||
uint32 totalGas, | ||
uint32 callbackGasLimit, | ||
address client | ||
) internal returns (FunctionsRouter.CallbackResult memory) { | ||
bytes memory payload = abi.encodeWithSelector( | ||
s_functionsRouter.exposed_callback.selector, | ||
reqId, | ||
resp, | ||
err, | ||
callbackGasLimit, | ||
client | ||
); | ||
(bool ok, bytes memory retData) = address(s_functionsRouter).call{gas: totalGas}(payload); | ||
assertTrue(ok, "callback should succeed"); | ||
return abi.decode(retData, (FunctionsRouter.CallbackResult)); | ||
} | ||
} | ||
|
||
contract MockClientSuccess { | ||
function handleOracleFulfillment(bytes32, bytes memory, bytes memory) external pure returns (bool) { | ||
return true; | ||
} | ||
} | ||
|
||
contract MockClientLargeReturn { | ||
function handleOracleFulfillment(bytes32, bytes memory, bytes memory) external pure returns (bytes memory) { | ||
// Return ~1,000 bytes. | ||
bytes memory largeData = new bytes(1000); | ||
for (uint i = 0; i < 1000; i++) { | ||
largeData[i] = bytes1(uint8(65 + (i % 26))); // Fill with A..Z. | ||
} | ||
return largeData; | ||
} | ||
} | ||
|
||
contract MockClientRevert { | ||
function handleOracleFulfillment(bytes32, bytes memory, bytes memory) external pure returns (bool) { | ||
revert("MockClientRevert"); | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
contracts/src/v0.8/functions/tests/v1_X/testhelpers/ZKSyncFunctionsRouterHarness.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.19; | ||
|
||
import {ZKSyncFunctionsRouter} from "../../../v1_3_0_zksync/ZKSyncFunctionsRouter.sol"; | ||
|
||
import {FunctionsRouter} from "../../../v1_0_0/FunctionsRouter.sol"; | ||
|
||
/// @title ZKSync Functions Router Test Harness | ||
/// @notice Contract to expose internal functions for testing purposes | ||
contract ZKSyncFunctionsRouterHarness is ZKSyncFunctionsRouter { | ||
constructor(address linkToken, FunctionsRouter.Config memory config) ZKSyncFunctionsRouter(linkToken, config) {} | ||
|
||
function exposed_callback( | ||
bytes32 requestId, | ||
bytes memory response, | ||
bytes memory err, | ||
uint32 callbackGasLimit, | ||
address client | ||
) public returns (CallbackResult memory) { | ||
// simply call the internal `_callback` method | ||
return super._callback(requestId, response, err, callbackGasLimit, client); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.