Skip to content

Commit

Permalink
feature(gas service): adding the gasOperator (#149)
Browse files Browse the repository at this point in the history
* feature(gas service): adding the gasOperator address for fees and refunds

* feature(gas service): added onlyOperator modifier

* fix(test): gas service deployment

* fix(gas-service): renaming to gasCollector

* fix(scripts): renaming to gasCollector

* feature(gas-service): adding amounts to collecting fees
  • Loading branch information
re1ro authored Aug 28, 2022
1 parent 09c9b16 commit a838abc
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 24 deletions.
33 changes: 26 additions & 7 deletions contracts/gas-service/AxelarGasService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ import '../util/Upgradable.sol';

// This should be owned by the microservice that is paying for gas.
contract AxelarGasService is Upgradable, IAxelarGasService {
address public immutable gasCollector;

constructor(address gasCollector_) {
gasCollector = gasCollector_;
}

modifier onlyCollector() {
if (msg.sender != gasCollector) revert NotCollector();

_;
}

// This is called on the source chain before calling the gateway to execute a remote contract.
function payGasForContractCall(
address sender,
Expand Down Expand Up @@ -117,18 +129,25 @@ contract AxelarGasService is Upgradable, IAxelarGasService {
emit NativeGasAdded(txHash, logIndex, msg.value, refundAddress);
}

function collectFees(address payable receiver, address[] calldata tokens) external onlyOwner {
function collectFees(
address payable receiver,
address[] calldata tokens,
uint256[] calldata amounts
) external onlyCollector {
if (receiver == address(0)) revert InvalidAddress();

for (uint256 i; i < tokens.length; i++) {
uint256 tokensLength = tokens.length;
if (tokensLength != amounts.length) revert InvalidAmounts();

for (uint256 i; i < tokensLength; i++) {
address token = tokens[i];
uint256 amount = amounts[i];
if (amount == 0) revert InvalidAmounts();

if (token == address(0)) {
uint256 amount = address(this).balance;
if (amount > 0) receiver.transfer(amount);
if (amount <= address(this).balance) receiver.transfer(amount);
} else {
uint256 amount = IERC20(token).balanceOf(address(this));
if (amount > 0) _safeTransfer(token, receiver, amount);
if (amount <= IERC20(token).balanceOf(address(this))) _safeTransfer(token, receiver, amount);
}
}
}
Expand All @@ -137,7 +156,7 @@ contract AxelarGasService is Upgradable, IAxelarGasService {
address payable receiver,
address token,
uint256 amount
) external onlyOwner {
) external onlyCollector {
if (receiver == address(0)) revert InvalidAddress();

if (token == address(0)) {
Expand Down
10 changes: 9 additions & 1 deletion contracts/interfaces/IAxelarGasService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ interface IAxelarGasService is IUpgradable {
error NothingReceived();
error TransferFailed();
error InvalidAddress();
error NotCollector();
error InvalidAmounts();

event GasPaidForContractCall(
address indexed sourceAddress,
Expand Down Expand Up @@ -114,11 +116,17 @@ interface IAxelarGasService is IUpgradable {
address refundAddress
) external payable;

function collectFees(address payable receiver, address[] calldata tokens) external;
function collectFees(
address payable receiver,
address[] calldata tokens,
uint256[] calldata amounts
) external;

function refund(
address payable receiver,
address token,
uint256 amount
) external;

function gasCollector() external returns (address);
}
5 changes: 5 additions & 0 deletions info/mainnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"chainId": 1,
"rpc": "https://mainnet.infura.io/v3/a4812158fbab4a2aaa849e6f4a6dc605",
"gateway": "0x4F4495243837681061C4743b74B3eEdf548D56A5",
"gasCollector": "0xa57adce1d2fe72949e4308867d894cd7e7de0ef2",
"gasReceiver": "0x4154CF6eea0633DD9c4933E76a077fD7E9260738",
"constAddressDeployer": "0x617179a15fEAa53Fa82ae80b0fc3E85b7359a748",
"tokenName": "Ether",
Expand All @@ -15,6 +16,7 @@
"chainId": 43114,
"rpc": "https://api.avax.network/ext/bc/C/rpc",
"gateway": "0x5029C0EFf6C34351a0CEc334542cDb22c7928f78",
"gasCollector": "0xa57adce1d2fe72949e4308867d894cd7e7de0ef2",
"gasReceiver": "0xB53C693544363912D2A034f70D9d98808D5E192a",
"constAddressDeployer": "0x617179a15fEAa53Fa82ae80b0fc3E85b7359a748",
"tokenName": "Avax",
Expand All @@ -26,6 +28,7 @@
"chainId": 250,
"gateway": "0x304acf330bbE08d1e512eefaa92F6a57871fD895",
"rpc": "https://rpc.ftm.tools/",
"gasCollector": "0xa57adce1d2fe72949e4308867d894cd7e7de0ef2",
"gasReceiver": "0x2879da536D9d107D6b92D95D7c4CFaA5De7088f4",
"constAddressDeployer": "0x617179a15fEAa53Fa82ae80b0fc3E85b7359a748",
"tokenName": "Fantom",
Expand All @@ -37,6 +40,7 @@
"chainId": 137,
"gateway": "0x6f015F16De9fC8791b234eF68D486d2bF203FBA8",
"rpc": "https://polygon-rpc.com/",
"gasCollector": "0xa57adce1d2fe72949e4308867d894cd7e7de0ef2",
"gasReceiver": "0xc8E0b617c388c7E800a7643adDD01218E14a727a",
"constAddressDeployer": "0x617179a15fEAa53Fa82ae80b0fc3E85b7359a748",
"tokenName": "Matic",
Expand All @@ -48,6 +52,7 @@
"chainId": 1284,
"gateway": "0x4F4495243837681061C4743b74B3eEdf548D56A5",
"rpc": "https://rpc.api.moonbeam.network",
"gasCollector": "0xa57adce1d2fe72949e4308867d894cd7e7de0ef2",
"gasReceiver": "0x27927CD55db998b720214205e598aA9AD614AEE3",
"constAddressDeployer": "0x617179a15fEAa53Fa82ae80b0fc3E85b7359a748",
"tokenName": "Glimmer",
Expand Down
6 changes: 6 additions & 0 deletions info/testnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"chainId": 3,
"rpc": "https://ropsten.infura.io/v3/a4812158fbab4a2aaa849e6f4a6dc605",
"gateway": "0xBC6fcce7c5487d43830a219CA6E7B83238B41e71",
"gasCollector": "0xB8Cd93C83A974649D76B1c19f311f639e62272BC",
"gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6",
"constAddressDeployer": "0x617179a15fEAa53Fa82ae80b0fc3E85b7359a748",
"tokenName": "Ether",
Expand All @@ -16,6 +17,7 @@
"chainId": 43113,
"rpc": "https://api.avax-test.network/ext/bc/C/rpc",
"gateway": "0xC249632c2D40b9001FE907806902f63038B737Ab",
"gasCollector": "0xB8Cd93C83A974649D76B1c19f311f639e62272BC",
"gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6",
"constAddressDeployer": "0x617179a15fEAa53Fa82ae80b0fc3E85b7359a748",
"tokenName": "Avax",
Expand All @@ -28,6 +30,7 @@
"chainId": 4002,
"gateway": "0x97837985Ec0494E7b9C71f5D3f9250188477ae14",
"rpc": "https://rpc.testnet.fantom.network",
"gasCollector": "0xB8Cd93C83A974649D76B1c19f311f639e62272BC",
"gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6",
"constAddressDeployer": "0x617179a15fEAa53Fa82ae80b0fc3E85b7359a748",
"tokenName": "Fantom",
Expand All @@ -40,6 +43,7 @@
"chainId": 80001,
"gateway": "0xBF62ef1486468a6bd26Dd669C06db43dEd5B849B",
"rpc": "https://polygon-mumbai.g.alchemy.com/v2/Ksd4J1QVWaOJAJJNbr_nzTcJBJU-6uP3",
"gasCollector": "0xB8Cd93C83A974649D76B1c19f311f639e62272BC",
"gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6",
"constAddressDeployer": "0x617179a15fEAa53Fa82ae80b0fc3E85b7359a748",
"tokenName": "Matic",
Expand All @@ -52,6 +56,7 @@
"chainId": 1287,
"gateway": "0x5769D84DD62a6fD969856c75c7D321b84d455929",
"rpc": "https://moonbeam-alpha.api.onfinality.io/public",
"gasCollector": "0xB8Cd93C83A974649D76B1c19f311f639e62272BC",
"gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6",
"constAddressDeployer": "0x617179a15fEAa53Fa82ae80b0fc3E85b7359a748",
"tokenName": "DEV",
Expand All @@ -64,6 +69,7 @@
"chainId": 97,
"gateway": "0x4D147dCb984e6affEEC47e44293DA442580A3Ec0",
"rpc": "https://data-seed-prebsc-1-s1.binance.org:8545/",
"gasCollector": "0xB8Cd93C83A974649D76B1c19f311f639e62272BC",
"gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6",
"constAddressDeployer": "0x617179a15fEAa53Fa82ae80b0fc3E85b7359a748",
"tokenName": "Binance Coin",
Expand Down
32 changes: 23 additions & 9 deletions scripts/deploy-upgradable.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,21 @@ const readlineSync = require('readline-sync');
const { outputJsonSync } = require('fs-extra');
const { defaultAbiCoder } = require('ethers/lib/utils');

function getImplementationArgs(contractName, chain) {
if (contractName === 'AxelarGasService') return [chain.gasCollector];
if (contractName === 'AxelarDepositService') return [];
throw new Error(`${contractName} is not supported.`);
}

function getInitArgs(contractName, chain) {
if (contractName == 'AxelarGasService') return '0x';
if (contractName == 'AxelarDepositService') return defaultAbiCoder.encode(['address', 'string'], [chain.gateway, chain.wrappedSymbol]);
if (contractName === 'AxelarGasService') return '0x';
if (contractName === 'AxelarDepositService') return defaultAbiCoder.encode(['address', 'string'], [chain.gateway, chain.wrappedSymbol]);
throw new Error(`${contractName} is not supported.`);
}

function getUpgradeArgs(contractName, chain) {
if (contractName == 'AxelarGasService') return '0x';
if (contractName == 'AxelarDepositService') return '0x';
if (contractName === 'AxelarGasService') return '0x';
if (contractName === 'AxelarDepositService') return '0x';
throw new Error(`${contractName} is not supported.`);
}

Expand All @@ -30,19 +37,25 @@ async function deploy(env, chains, wallet, artifactPath, contractName, deployTo)
const implementationJson = require(implementationPath);
const proxyJson = require(proxyPath);
for (const chain of chains) {
if (deployTo.length > 0 && deployTo.find((name) => chain.name == name) == null) continue;
if (deployTo.length > 0 && deployTo.find((name) => chain.name === name) === null) continue;
const rpc = chain.rpc;
const provider = getDefaultProvider(rpc);
console.log(`Deployer has ${(await provider.getBalance(wallet.address)) / 1e18} ${chain.tokenSymbol} on ${chain.name}.`);
}
const anwser = readlineSync.question('Proceed with deployment? (y/n). ');
if (anwser != 'y') return;
if (anwser !== 'y') return;
for (const chain of chains) {
if (deployTo.length > 0 && deployTo.find((name) => chain.name == name) == null) continue;
if (deployTo.length > 0 && deployTo.find((name) => chain.name === name) === null) continue;
const rpc = chain.rpc;
const provider = getDefaultProvider(rpc);
if (chain[contractName]) {
await upgradeUpgradable(chain[contractName], implementationJson, getUpgradeArgs(contractName, chain), wallet.connect(provider));
await upgradeUpgradable(
wallet.connect(provider),
chain[contractName],
implementationJson,
getImplementationArgs(contractName, chain),
getUpgradeArgs(contractName, chain),
);
console.log(`${chain.name} | Upgraded.`);
} else {
const key = contractName;
Expand All @@ -51,6 +64,7 @@ async function deploy(env, chains, wallet, artifactPath, contractName, deployTo)
wallet.connect(provider),
implementationJson,
proxyJson,
getImplementationArgs(contractName, chain),
getInitArgs(contractName, chain),
key,
);
Expand All @@ -63,7 +77,7 @@ async function deploy(env, chains, wallet, artifactPath, contractName, deployTo)

if (require.main === module) {
const env = process.argv[2];
if (env == null || (env != 'testnet' && env != 'mainnet'))
if (env === null || (env !== 'testnet' && env !== 'mainnet'))
throw new Error('Need to specify tesntet or local as an argument to this script.');

const chains = require(`../info/${env}.json`);
Expand Down
4 changes: 2 additions & 2 deletions scripts/upgradable.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ async function deployUpgradable(
return new Contract(proxy.address, implementationJson.abi, wallet);
}

async function upgradeUpgradable(proxyAddress, contractJson, setupParams, wallet) {
async function upgradeUpgradable(wallet, proxyAddress, contractJson, implementationParams = [], setupParams = '0x') {
const proxy = new Contract(proxyAddress, IUpgradable.abi, wallet);

const implementationFactory = new ContractFactory(contractJson.abi, contractJson.bytecode, wallet);

const implementation = await implementationFactory.deploy();
const implementation = await implementationFactory.deploy(...implementationParams);
await implementation.deployed();

const implementationCode = await wallet.provider.getCode(implementation.address);
Expand Down
19 changes: 15 additions & 4 deletions test/gmp/AxelarGasService.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe('AxelarGasService', () => {
beforeEach(async () => {
const constAddressDeployer = await deployContract(ownerWallet, ConstAddressDeployer);

gasService = await deployUpgradable(constAddressDeployer.address, ownerWallet, GasService, GasServiceProxy);
gasService = await deployUpgradable(constAddressDeployer.address, ownerWallet, GasService, GasServiceProxy, [ownerWallet.address]);

const name = 'testToken';
const symbol = 'testToken';
Expand Down Expand Up @@ -201,17 +201,28 @@ describe('AxelarGasService', () => {
.and.to.emit(testToken, 'Transfer')
.withArgs(gasService.address, userWallet.address, gasFeeAmount);

await expect(gasService.connect(userWallet).collectFees(ownerWallet.address, [ADDRESS_ZERO, testToken.address])).to.be.reverted;
await expect(
gasService
.connect(userWallet)
.collectFees(ownerWallet.address, [ADDRESS_ZERO, testToken.address], [nativeGasFeeAmount, gasFeeAmount]),
).to.be.reverted;

await expect(await gasService.connect(ownerWallet).collectFees(ownerWallet.address, [ADDRESS_ZERO, testToken.address]))
await expect(
await gasService
.connect(ownerWallet)
.collectFees(ownerWallet.address, [ADDRESS_ZERO, testToken.address], [nativeGasFeeAmount, gasFeeAmount]),
)
.to.changeEtherBalance(ownerWallet, nativeGasFeeAmount)
.and.to.emit(testToken, 'Transfer')
.withArgs(gasService.address, ownerWallet.address, gasFeeAmount);
});

it('should upgrade the gas receiver implementation', async () => {
const prevImpl = await gasService.implementation();
await expect(upgradeUpgradable(gasService.address, GasService, '0x', ownerWallet)).to.emit(gasService, 'Upgraded');
await expect(upgradeUpgradable(ownerWallet, gasService.address, GasService, [ownerWallet.address])).to.emit(
gasService,
'Upgraded',
);

const newImpl = await gasService.implementation();
expect(await gasService.owner()).to.be.equal(ownerWallet.address);
Expand Down
4 changes: 3 additions & 1 deletion test/gmp/GeneralMessagePassing.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ describe('GeneralMessagePassing', () => {
destinationChainGateway = await deployGateway();
const constAddressDeployer = await deployContract(ownerWallet, ConstAddressDeployer);

sourceChainGasService = await deployUpgradable(constAddressDeployer.address, ownerWallet, GasService, GasServiceProxy);
sourceChainGasService = await deployUpgradable(constAddressDeployer.address, ownerWallet, GasService, GasServiceProxy, [
ownerWallet.address,
]);
tokenA = await deployContract(ownerWallet, MintableCappedERC20, [nameA, symbolA, decimals, capacity]);

tokenB = await deployContract(ownerWallet, MintableCappedERC20, [nameB, symbolB, decimals, capacity]);
Expand Down

0 comments on commit a838abc

Please sign in to comment.