Governance app contracts pay for interchain gas when dispatching messages (#302)

* Tests for xAppConnectionManager

* Clean up tests, have InterchainGasPaymaster implement IInterchainGasPaymaster

* Nit

* Add leafIndex return value to dispatch

* Add to XAppConnectionClient, start on _dispatchToRemoteRouterAndPayForGas needs natspec though

* Prettier, finish natspec

* Add periods to natspec

* Rm describe.only

* _paymentAmount -> _gasPayment

* PR comments, rename dispatchToRemoteRouterAndPayForGas -> dispatchToRemoteRouterWithGas

* Add interchain gas paymaster to typescript/hardhat's TestAbacusDeploy, use interchain gas payment in GovernanceRouter

* Prettier

* nit

* PR comments

* Bump hardhat 0.0.5 -> 0.0.6

* Refactor tests

* reduce diff

* rm describe's .only

* Didn't need to bump hardhat after all
pull/321/head
Trevor Porter 3 years ago committed by GitHub
parent 2790d2bd9e
commit 1474a068a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 27
      solidity/apps/contracts/governance/GovernanceRouter.sol
  2. 62
      solidity/apps/test/governance/governanceRouter.test.ts
  3. 16
      typescript/hardhat/src/TestAbacusDeploy.ts

@ -187,20 +187,22 @@ contract GovernanceRouter is Version0, Router {
// ============ External Remote Functions ============
/**
* @notice Dispatch calls on a remote chain via the remote GovernanceRouter
* @notice Dispatch calls on a remote chain via the remote GovernanceRouter.
* Any value paid to this function is used to pay for message processing on the remote chain.
* @param _destination The domain of the remote chain
* @param _calls The calls
*/
function callRemote(
uint32 _destination,
GovernanceMessage.Call[] calldata _calls
) external onlyGovernor onlyNotInRecovery {
) external payable onlyGovernor onlyNotInRecovery {
bytes memory _msg = GovernanceMessage.formatCalls(_calls);
_dispatchToRemoteRouter(_destination, _msg);
_dispatchToRemoteRouterWithGas(_destination, _msg, msg.value);
}
/**
* @notice Enroll a remote router on a remote router.
* @notice Enroll a remote router on a remote router. Any value paid to this
* function is used to pay for message processing on the remote chain.
* @param _destination The domain of the enroller
* @param _domain The domain of the enrollee
* @param _router The address of the enrollee
@ -209,43 +211,46 @@ contract GovernanceRouter is Version0, Router {
uint32 _destination,
uint32 _domain,
bytes32 _router
) external onlyGovernor onlyNotInRecovery {
) external payable onlyGovernor onlyNotInRecovery {
bytes memory _msg = GovernanceMessage.formatEnrollRemoteRouter(
_domain,
_router
);
_dispatchToRemoteRouter(_destination, _msg);
_dispatchToRemoteRouterWithGas(_destination, _msg, msg.value);
}
/**
* @notice Sets the xAppConnectionManager of a remote router.
* @notice Sets the xAppConnectionManager of a remote router. Any value paid to this
* function is used to pay for message processing on the remote chain.
* @param _destination The domain of router on which to set the xAppConnectionManager
* @param _xAppConnectionManager The address of the xAppConnectionManager contract
*/
function setXAppConnectionManagerRemote(
uint32 _destination,
address _xAppConnectionManager
) external onlyGovernor onlyNotInRecovery {
) external payable onlyGovernor onlyNotInRecovery {
bytes memory _msg = GovernanceMessage.formatSetXAppConnectionManager(
TypeCasts.addressToBytes32(_xAppConnectionManager)
);
_dispatchToRemoteRouter(_destination, _msg);
_dispatchToRemoteRouterWithGas(_destination, _msg, msg.value);
}
/**
* @notice Sets the governor of a remote router.
* @notice Sets the governor of a remote router. Any value paid to this
* function is used to pay for message processing on the remote chain.
* @param _destination The domain of router on which to set the governor
* @param _governor The address of the new governor
*/
function setGovernorRemote(uint32 _destination, address _governor)
external
payable
onlyGovernor
onlyNotInRecovery
{
bytes memory _msg = GovernanceMessage.formatSetGovernor(
TypeCasts.addressToBytes32(_governor)
);
_dispatchToRemoteRouter(_destination, _msg);
_dispatchToRemoteRouterWithGas(_destination, _msg, msg.value);
}
// ============ Public Functions ============

@ -1,5 +1,6 @@
import { ethers, abacus } from 'hardhat';
import { expect } from 'chai';
import { InterchainGasPaymaster, Outbox } from '@abacus-network/core';
import { utils } from '@abacus-network/utils';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
@ -71,7 +72,6 @@ describe('GovernanceRouter', async () => {
const inbox = abacus.inbox(localDomain, remoteDomain);
await inbox.setMessageProven(fakeMessage);
// Expect inbox processing to fail when reverting in handle
await inbox.testProcess(fakeMessage);
expect(await router.governor()).to.equal(ethers.constants.AddressZero);
});
@ -474,4 +474,64 @@ describe('GovernanceRouter', async () => {
);
});
});
describe('interchain gas payments for dispatched messages', async () => {
const testInterchainGasPayment = 123456789;
let outbox: Outbox;
let interchainGasPaymaster: InterchainGasPaymaster;
before(() => {
outbox = abacus.outbox(localDomain);
interchainGasPaymaster = abacus.interchainGasPaymaster(localDomain);
});
it('allows interchain gas payment for remote calls', async () => {
const leafIndex = await outbox.count();
const call = await formatCall(testSet, 'set', [13]);
await expect(
await router.callRemote(domains[1], [call], {
value: testInterchainGasPayment,
}),
)
.to.emit(interchainGasPaymaster, 'GasPayment')
.withArgs(leafIndex, testInterchainGasPayment);
});
it('allows interchain gas payment when setting a remote governor', async () => {
const leafIndex = await outbox.count();
await expect(
router.setGovernorRemote(remoteDomain, governor.address, {
value: testInterchainGasPayment,
}),
)
.to.emit(interchainGasPaymaster, 'GasPayment')
.withArgs(leafIndex, testInterchainGasPayment);
});
it('allows interchain gas payment when setting a remote xAppConnectionManager', async () => {
const leafIndex = await outbox.count();
await expect(
router.setXAppConnectionManagerRemote(
remoteDomain,
ethers.constants.AddressZero,
{ value: testInterchainGasPayment },
),
)
.to.emit(interchainGasPaymaster, 'GasPayment')
.withArgs(leafIndex, testInterchainGasPayment);
});
it('allows interchain gas payment when enrolling a remote router', async () => {
const leafIndex = await outbox.count();
const newRouter = utils.addressToBytes32(router.address);
await expect(
router.enrollRemoteRouterRemote(remoteDomain, testDomain, newRouter, {
value: testInterchainGasPayment,
}),
)
.to.emit(interchainGasPaymaster, 'GasPayment')
.withArgs(leafIndex, testInterchainGasPayment);
});
});
});

@ -3,6 +3,8 @@ import { types } from "@abacus-network/utils";
import {
Outbox,
Outbox__factory,
InterchainGasPaymaster,
InterchainGasPaymaster__factory,
ValidatorManager,
ValidatorManager__factory,
UpgradeBeaconController,
@ -25,6 +27,7 @@ export type TestAbacusInstance = {
xAppConnectionManager: XAppConnectionManager;
upgradeBeaconController: UpgradeBeaconController;
inboxes: Record<types.Domain, TestInbox>;
interchainGasPaymaster: InterchainGasPaymaster;
};
export class TestAbacusDeploy extends TestDeploy<
@ -73,6 +76,14 @@ export class TestAbacusDeploy extends TestDeploy<
const xAppConnectionManager = await xAppConnectionManagerFactory.deploy();
await xAppConnectionManager.setOutbox(outbox.address);
const interchainGasPaymasterFactory = new InterchainGasPaymaster__factory(
signer
);
const interchainGasPaymaster = await interchainGasPaymasterFactory.deploy();
await xAppConnectionManager.setInterchainGasPaymaster(
interchainGasPaymaster.address
);
const inboxFactory = new TestInbox__factory(signer);
const inboxes: Record<types.Domain, TestInbox> = {};
// this.remotes reads this.instances which has not yet been set.
@ -92,6 +103,7 @@ export class TestAbacusDeploy extends TestDeploy<
return {
outbox,
xAppConnectionManager,
interchainGasPaymaster,
validatorManager,
inboxes,
upgradeBeaconController,
@ -120,6 +132,10 @@ export class TestAbacusDeploy extends TestDeploy<
return this.instances[local].inboxes[remote];
}
interchainGasPaymaster(domain: types.Domain): InterchainGasPaymaster {
return this.instances[domain].interchainGasPaymaster;
}
xAppConnectionManager(domain: types.Domain): XAppConnectionManager {
return this.instances[domain].xAppConnectionManager;
}

Loading…
Cancel
Save