-
Notifications
You must be signed in to change notification settings - Fork 161
Allow batch transactions using multicall in the GNS #485
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bd0c99d
026c864
d2a9f72
94b8464
8373b57
2f18eb0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
|
||
pragma solidity ^0.7.3; | ||
pragma experimental ABIEncoderV2; | ||
|
||
/** | ||
* @title Multicall interface | ||
* @notice Enables calling multiple methods in a single call to the contract | ||
*/ | ||
interface IMulticall { | ||
/** | ||
* @notice Call multiple functions in the current contract and return the data from all of them if they all succeed | ||
* @param data The encoded function data for each of the calls to make to this contract | ||
* @return results The results from each of the calls passed in via data | ||
*/ | ||
function multicall(bytes[] calldata data) external returns (bytes[] memory results); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
|
||
pragma solidity ^0.7.3; | ||
pragma experimental ABIEncoderV2; | ||
|
||
import "./IMulticall.sol"; | ||
|
||
// Inspired by https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/base/Multicall.sol | ||
// Note: Removed payable from the multicall | ||
|
||
/** | ||
* @title Multicall | ||
* @notice Enables calling multiple methods in a single call to the contract | ||
*/ | ||
abstract contract Multicall is IMulticall { | ||
/// @inheritdoc IMulticall | ||
function multicall(bytes[] calldata data) external override returns (bytes[] memory results) { | ||
results = new bytes[](data.length); | ||
for (uint256 i = 0; i < data.length; i++) { | ||
(bool success, bytes memory result) = address(this).delegatecall(data[i]); | ||
|
||
if (!success) { | ||
// Next 5 lines from https://ethereum.stackexchange.com/a/83577 | ||
if (result.length < 68) revert(); | ||
assembly { | ||
result := add(result, 0x04) | ||
} | ||
revert(abi.decode(result, (string))); | ||
} | ||
|
||
results[i] = result; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,11 +2,11 @@ import { expect } from 'chai' | |
import { ethers, ContractTransaction, BigNumber, Event } from 'ethers' | ||
|
||
import { GNS } from '../build/types/GNS' | ||
import { getAccounts, randomHexBytes, Account, toGRT } from './lib/testHelpers' | ||
import { NetworkFixture } from './lib/fixtures' | ||
import { GraphToken } from '../build/types/GraphToken' | ||
import { Curation } from '../build/types/Curation' | ||
|
||
import { getAccounts, randomHexBytes, Account, toGRT } from './lib/testHelpers' | ||
import { NetworkFixture } from './lib/fixtures' | ||
import { toBN, formatGRT } from './lib/testHelpers' | ||
|
||
interface Subgraph { | ||
|
@@ -983,4 +983,68 @@ describe('GNS', () => { | |
await gns.connect(me.signer).mintNSignal(me.address, 1, toGRT('10'), 0) | ||
}) | ||
}) | ||
|
||
describe('batch calls', function () { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looks good. i would like to add a few tests to play it safe. i want to add the following sequences:
I think it will be fine. I can write the tests for these There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea |
||
it('should publish new subgraph and mint signal in single transaction', async function () { | ||
// Create a subgraph | ||
const tx1 = await gns.populateTransaction.publishNewSubgraph( | ||
me.address, | ||
subgraph0.subgraphDeploymentID, | ||
subgraph0.versionMetadata, | ||
subgraph0.subgraphMetadata, | ||
) | ||
// Curate on the subgraph | ||
const subgraphNumber = await gns.graphAccountSubgraphNumbers(me.address) | ||
const tx2 = await gns.populateTransaction.mintNSignal( | ||
me.address, | ||
subgraphNumber, | ||
toGRT('90000'), | ||
0, | ||
) | ||
|
||
// Batch send transaction | ||
await gns.connect(me.signer).multicall([tx1.data, tx2.data]) | ||
}) | ||
|
||
it('should revert if batching a call to non-authorized function', async function () { | ||
// Call a forbidden function | ||
const tx1 = await gns.populateTransaction.setOwnerTaxPercentage(100) | ||
|
||
// Create a subgraph | ||
const tx2 = await gns.populateTransaction.publishNewSubgraph( | ||
me.address, | ||
subgraph0.subgraphDeploymentID, | ||
subgraph0.versionMetadata, | ||
subgraph0.subgraphMetadata, | ||
) | ||
|
||
// Batch send transaction | ||
const tx = gns.connect(me.signer).multicall([tx1.data, tx2.data]) | ||
await expect(tx).revertedWith('Caller must be Controller governor') | ||
}) | ||
|
||
it('should revert if trying to call a private function', async function () { | ||
// Craft call a private function | ||
const hash = ethers.utils.id('_setOwnerTaxPercentage(uint32)') | ||
const functionHash = hash.slice(0, 10) | ||
const calldata = ethers.utils.arrayify( | ||
ethers.utils.defaultAbiCoder.encode(['uint32'], ['100']), | ||
) | ||
const bogusPayload = ethers.utils.concat([functionHash, calldata]) | ||
|
||
// Create a subgraph | ||
const tx2 = await gns.populateTransaction.publishNewSubgraph( | ||
me.address, | ||
subgraph0.subgraphDeploymentID, | ||
subgraph0.versionMetadata, | ||
subgraph0.subgraphMetadata, | ||
) | ||
|
||
// Batch send transaction | ||
const tx = gns.connect(me.signer).multicall([bogusPayload, tx2.data]) | ||
await expect(tx).revertedWith( | ||
"function selector was not recognized and there's no fallback function", | ||
) | ||
}) | ||
}) | ||
}) |
Uh oh!
There was an error while loading. Please reload this page.