Skip to content

Commit

Permalink
refactor: add gas portal to l1 contract addresses (#5265)
Browse files Browse the repository at this point in the history
Add `gasPortalAddress` to config and optionally deploy the portal and
gas token at bootstrap. This enables reading the correct canonical gas
token everywhere.

Fix #5022
  • Loading branch information
alexghr authored Mar 19, 2024
1 parent 57596d7 commit 640c89a
Show file tree
Hide file tree
Showing 23 changed files with 266 additions and 143 deletions.
13 changes: 6 additions & 7 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ setup_env: &setup_env
command: ./build-system/scripts/setup_env "$CIRCLE_SHA1" "$CIRCLE_TAG" "$CIRCLE_JOB" "$CIRCLE_REPOSITORY_URL" "$CIRCLE_BRANCH" "$CIRCLE_PULL_REQUEST"

defaults_e2e_test: &defaults_e2e_test
docker:
- image: aztecprotocol/alpine-build-image
resource_class: small
docker:
- image: aztecprotocol/alpine-build-image
resource_class: small

jobs:
# Dynamically filter our code, quickly figuring out which jobs we can skip.
Expand Down Expand Up @@ -868,7 +868,6 @@ jobs:
aztec_manifest_key: end-to-end
<<: *defaults_e2e_test


e2e-outbox:
docker:
- image: aztecprotocol/alpine-build-image
Expand Down Expand Up @@ -958,7 +957,7 @@ jobs:
- *setup_env
- run:
name: "Test"
command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_fees.test.ts
command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_fees.test.ts ENABLE_GAS=1
aztec_manifest_key: end-to-end
<<: *defaults_e2e_test

Expand All @@ -968,7 +967,7 @@ jobs:
- *setup_env
- run:
name: "Test"
command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_dapp_subscription.test.ts
command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_dapp_subscription.test.ts ENABLE_GAS=1
aztec_manifest_key: end-to-end
<<: *defaults_e2e_test

Expand Down Expand Up @@ -1221,7 +1220,7 @@ defaults: &defaults
- slack/notify:
event: fail
branch_pattern: "master"

bb_acir_tests: &bb_acir_tests
requires:
- barretenberg-x86_64-linux-clang-assert
Expand Down
6 changes: 6 additions & 0 deletions yarn-project/archiver/src/archiver/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ export function getConfigEnvVars(): ArchiverConfig {
INBOX_CONTRACT_ADDRESS,
OUTBOX_CONTRACT_ADDRESS,
REGISTRY_CONTRACT_ADDRESS,
GAS_TOKEN_CONTRACT_ADDRESS,
GAS_PORTAL_CONTRACT_ADDRESS,
DATA_DIRECTORY,
} = process.env;
// Populate the relevant addresses for use by the archiver.
Expand All @@ -73,6 +75,10 @@ export function getConfigEnvVars(): ArchiverConfig {
registryAddress: REGISTRY_CONTRACT_ADDRESS ? EthAddress.fromString(REGISTRY_CONTRACT_ADDRESS) : EthAddress.ZERO,
inboxAddress: INBOX_CONTRACT_ADDRESS ? EthAddress.fromString(INBOX_CONTRACT_ADDRESS) : EthAddress.ZERO,
outboxAddress: OUTBOX_CONTRACT_ADDRESS ? EthAddress.fromString(OUTBOX_CONTRACT_ADDRESS) : EthAddress.ZERO,
gasTokenAddress: GAS_TOKEN_CONTRACT_ADDRESS ? EthAddress.fromString(GAS_TOKEN_CONTRACT_ADDRESS) : EthAddress.ZERO,
gasPortalAddress: GAS_PORTAL_CONTRACT_ADDRESS
? EthAddress.fromString(GAS_PORTAL_CONTRACT_ADDRESS)
: EthAddress.ZERO,
};
return {
rpcUrl: ETHEREUM_HOST || 'http://127.0.0.1:8545/',
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/aztec.js/src/contract/contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ describe('Contract Class', () => {
registryAddress: EthAddress.random(),
inboxAddress: EthAddress.random(),
outboxAddress: EthAddress.random(),
gasTokenAddress: EthAddress.random(),
gasPortalAddress: EthAddress.random(),
};
const mockNodeInfo: NodeInfo = {
nodeVersion: 'vx.x.x',
Expand Down
24 changes: 16 additions & 8 deletions yarn-project/aztec.js/src/fee/native_fee_payment_method.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,41 @@
import { FunctionCall } from '@aztec/circuit-types';
import { FunctionData } from '@aztec/circuits.js';
import { AztecAddress, FunctionData } from '@aztec/circuits.js';
import { FunctionSelector } from '@aztec/foundation/abi';
import { Fr } from '@aztec/foundation/fields';
import { GasTokenAddress } from '@aztec/protocol-contracts/gas-token';
import { getCanonicalGasTokenAddress } from '@aztec/protocol-contracts/gas-token';

import { Wallet } from '../account/wallet.js';
import { FeePaymentMethod } from './fee_payment_method.js';

/**
* Pay fee directly in the native gas token.
*/
export class NativeFeePaymentMethod implements FeePaymentMethod {
static #GAS_TOKEN = GasTokenAddress;
#gasTokenAddress: AztecAddress;

constructor() {}
private constructor(canonicalGasTokenAddress: AztecAddress) {
this.#gasTokenAddress = canonicalGasTokenAddress;
}

static async create(wallet: Wallet): Promise<NativeFeePaymentMethod> {
const nodeInfo = await wallet.getNodeInfo();
return new NativeFeePaymentMethod(getCanonicalGasTokenAddress(nodeInfo.l1ContractAddresses.gasPortalAddress));
}

/**
* Gets the native gas asset used to pay the fee.
* @returns The asset used to pay the fee.
*/
getAsset() {
return NativeFeePaymentMethod.#GAS_TOKEN;
return this.#gasTokenAddress;
}

/**
* The contract responsible for fee payment. This will be the same as the asset.
* @returns The contract address responsible for holding the fee payment.
*/
getPaymentContract() {
return NativeFeePaymentMethod.#GAS_TOKEN;
return this.#gasTokenAddress;
}

/**
Expand All @@ -46,12 +54,12 @@ export class NativeFeePaymentMethod implements FeePaymentMethod {
getFunctionCalls(feeLimit: Fr): Promise<FunctionCall[]> {
return Promise.resolve([
{
to: NativeFeePaymentMethod.#GAS_TOKEN,
to: this.#gasTokenAddress,
functionData: new FunctionData(FunctionSelector.fromSignature('check_balance(Field)'), false),
args: [feeLimit],
},
{
to: NativeFeePaymentMethod.#GAS_TOKEN,
to: this.#gasTokenAddress,
functionData: new FunctionData(FunctionSelector.fromSignature('pay_fee(Field)'), false),
args: [feeLimit],
},
Expand Down
1 change: 1 addition & 0 deletions yarn-project/aztec/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@aztec/noir-compiler": "workspace:^",
"@aztec/noir-contracts.js": "workspace:^",
"@aztec/p2p": "workspace:^",
"@aztec/protocol-contracts": "workspace:^",
"@aztec/pxe": "workspace:^",
"abitype": "^0.8.11",
"commander": "^11.1.0",
Expand Down
6 changes: 4 additions & 2 deletions yarn-project/aztec/src/bin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const debugLogger = createDebugLogger('aztec:cli');
const packageJsonPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../package.json');
const cliVersion: string = JSON.parse(readFileSync(packageJsonPath).toString()).version;

const { TEST_ACCOUNTS = 'true', PORT = '8080' } = process.env;
const { TEST_ACCOUNTS = 'true', PORT = '8080', ENABLE_GAS = '' } = process.env;

/** CLI & full node main entrypoint */
async function main() {
Expand All @@ -32,7 +32,9 @@ async function main() {
// If no CLI arguments were provided, run aztec full node for sandbox usage.
userLog(`${splash}\n${github}\n\n`);
userLog(`Setting up Aztec Sandbox v${cliVersion}, please stand by...`);
const { aztecNodeConfig, node, pxe, stop } = await createSandbox();
const { aztecNodeConfig, node, pxe, stop } = await createSandbox({
enableGas: ['true', '1'].includes(ENABLE_GAS),
});
installSignalHandlers(userLog, [stop]);

// Deploy test accounts by default
Expand Down
81 changes: 75 additions & 6 deletions yarn-project/aztec/src/sandbox.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#!/usr/bin/env -S node --no-warnings
import { AztecNodeConfig, AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node';
import { AztecAddress, SignerlessWallet, Wallet } from '@aztec/aztec.js';
import { deployInstance, registerContractClass } from '@aztec/aztec.js/deployment';
import { AztecNode } from '@aztec/circuit-types';
import {
DeployL1Contracts,
L1ContractAddresses,
L1ContractArtifactsForDeployment,
NULL_KEY,
createEthereumChain,
Expand All @@ -13,18 +16,23 @@ import { retryUntil } from '@aztec/foundation/retry';
import {
AvailabilityOracleAbi,
AvailabilityOracleBytecode,
GasPortalAbi,
GasPortalBytecode,
InboxAbi,
InboxBytecode,
OutboxAbi,
OutboxBytecode,
PortalERC20Abi,
PortalERC20Bytecode,
RegistryAbi,
RegistryBytecode,
RollupAbi,
RollupBytecode,
} from '@aztec/l1-artifacts';
import { getCanonicalGasToken } from '@aztec/protocol-contracts/gas-token';
import { PXEServiceConfig, createPXEService, getPXEServiceConfig } from '@aztec/pxe';

import { HDAccount, PrivateKeyAccount, createPublicClient, http as httpViemTransport } from 'viem';
import { HDAccount, PrivateKeyAccount, createPublicClient, getContract, http as httpViemTransport } from 'viem';
import { mnemonicToAccount } from 'viem/accounts';
import { foundry } from 'viem/chains';

Expand Down Expand Up @@ -98,21 +106,77 @@ export async function deployContractsToL1(
contractAbi: RollupAbi,
contractBytecode: RollupBytecode,
},
gasToken: {
contractAbi: PortalERC20Abi,
contractBytecode: PortalERC20Bytecode,
},
gasPortal: {
contractAbi: GasPortalAbi,
contractBytecode: GasPortalBytecode,
},
};

aztecNodeConfig.l1Contracts = (
await waitThenDeploy(aztecNodeConfig, () =>
deployL1Contracts(aztecNodeConfig.rpcUrl, hdAccount, localAnvil, contractDeployLogger, l1Artifacts),
)
).l1ContractAddresses;
const l1Contracts = await waitThenDeploy(aztecNodeConfig, () =>
deployL1Contracts(aztecNodeConfig.rpcUrl, hdAccount, localAnvil, contractDeployLogger, l1Artifacts),
);
await initL1GasPortal(l1Contracts, getCanonicalGasToken(l1Contracts.l1ContractAddresses.gasPortalAddress).address);

aztecNodeConfig.l1Contracts = l1Contracts.l1ContractAddresses;

return aztecNodeConfig.l1Contracts;
}

/**
* Initializes the portal between L1 and L2 used to pay for gas.
* @param l1Data - The deployed L1 data.
*/
async function initL1GasPortal(
{ walletClient, l1ContractAddresses }: DeployL1Contracts,
l2GasTokenAddress: AztecAddress,
) {
const gasPortal = getContract({
address: l1ContractAddresses.gasPortalAddress.toString(),
abi: GasPortalAbi,
client: walletClient,
});

await gasPortal.write.initialize(
[
l1ContractAddresses.registryAddress.toString(),
l1ContractAddresses.gasTokenAddress.toString(),
l2GasTokenAddress.toString(),
],
{} as any,
);

logger(
`Initialized Gas Portal at ${l1ContractAddresses.gasPortalAddress} to bridge between L1 ${l1ContractAddresses.gasTokenAddress} to L2 ${l2GasTokenAddress}`,
);
}

/**
* Deploys the contract to pay for gas on L2.
*/
async function deployCanonicalL2GasToken(deployer: Wallet, l1ContractAddresses: L1ContractAddresses) {
const gasPortalAddress = l1ContractAddresses.gasPortalAddress;
const canonicalGasToken = getCanonicalGasToken(gasPortalAddress);

if (await deployer.isContractClassPubliclyRegistered(canonicalGasToken.contractClass.id)) {
return;
}

await (await registerContractClass(deployer, canonicalGasToken.artifact)).send().wait();
await deployInstance(deployer, canonicalGasToken.instance).send().wait();

logger(`Deployed Gas Token on L2 at ${canonicalGasToken.address}`);
}

/** Sandbox settings. */
export type SandboxConfig = AztecNodeConfig & {
/** Mnemonic used to derive the L1 deployer private key.*/
l1Mnemonic: string;
/** Enable the contracts to track and pay for gas */
enableGas: boolean;
};

/**
Expand All @@ -135,6 +199,11 @@ export async function createSandbox(config: Partial<SandboxConfig> = {}) {
const node = await createAztecNode(aztecNodeConfig);
const pxe = await createAztecPXE(node);

if (config.enableGas) {
const deployer = new SignerlessWallet(pxe);
await deployCanonicalL2GasToken(deployer, aztecNodeConfig.l1Contracts);
}

const stop = async () => {
await pxe.stop();
await node.stop();
Expand Down
3 changes: 3 additions & 0 deletions yarn-project/aztec/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
{
"path": "../p2p"
},
{
"path": "../protocol-contracts"
},
{
"path": "../pxe"
}
Expand Down
17 changes: 16 additions & 1 deletion yarn-project/cli/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ import { type L1ContractArtifactsForDeployment } from '@aztec/aztec.js/ethereum'
import { type PXE } from '@aztec/aztec.js/interfaces/pxe';
import { DebugLogger, LogFn } from '@aztec/foundation/log';
import { NoirPackageConfig } from '@aztec/foundation/noir';
import { AvailabilityOracleAbi, AvailabilityOracleBytecode } from '@aztec/l1-artifacts';
import {
AvailabilityOracleAbi,
AvailabilityOracleBytecode,
GasPortalAbi,
GasPortalBytecode,
PortalERC20Abi,
PortalERC20Bytecode,
} from '@aztec/l1-artifacts';

import TOML from '@iarna/toml';
import { CommanderError, InvalidArgumentError } from 'commander';
Expand Down Expand Up @@ -85,6 +92,14 @@ export async function deployAztecContracts(
contractAbi: RollupAbi,
contractBytecode: RollupBytecode,
},
gasToken: {
contractAbi: PortalERC20Abi,
contractBytecode: PortalERC20Bytecode,
},
gasPortal: {
contractAbi: GasPortalAbi,
contractBytecode: GasPortalBytecode,
},
};
return await deployL1Contracts(chain.rpcUrl, account, chain.chainInfo, debugLogger, l1Artifacts);
}
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/end-to-end/Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,11 @@ e2e-avm-simulator:
DO +E2E_TEST --test=e2e_avm_simulator.test.ts

e2e-fees:
ENV ENABLE_GAS=1
DO +E2E_TEST --test=e2e_fees.test.ts

e2e-dapp-subscription:
ENV ENABLE_GAS=1
DO +E2E_TEST --test=e2e_dapp_subscription.test.ts

pxe:
Expand Down
1 change: 1 addition & 0 deletions yarn-project/end-to-end/scripts/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ services:
PXE_BLOCK_POLLING_INTERVAL_MS: 50
ARCHIVER_VIEM_POLLING_INTERVAL_MS: 500
AVM_ENABLED: ${AVM_ENABLED:-}
ENABLE_GAS: ${ENABLE_GAS:-}
ports:
- '8080:8080'

Expand Down
11 changes: 7 additions & 4 deletions yarn-project/end-to-end/src/e2e_dapp_subscription.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
FPCContract,
GasTokenContract,
} from '@aztec/noir-contracts.js';
import { GasTokenAddress } from '@aztec/protocol-contracts/gas-token';
import { getCanonicalGasTokenAddress } from '@aztec/protocol-contracts/gas-token';

import { jest } from '@jest/globals';

Expand Down Expand Up @@ -62,13 +62,16 @@ describe('e2e_dapp_subscription', () => {

beforeAll(async () => {
process.env.PXE_URL = '';
e2eContext = await setup(3, { deployProtocolContracts: true });
e2eContext = await setup(3);
await publicDeployAccounts(e2eContext.wallet, e2eContext.accounts);

const { wallets, accounts, aztecNode } = e2eContext;
const { wallets, accounts, aztecNode, deployL1ContractsValues } = e2eContext;

// this should be a SignerlessWallet but that can't call public functions directly
gasTokenContract = await GasTokenContract.at(GasTokenAddress, wallets[0]);
gasTokenContract = await GasTokenContract.at(
getCanonicalGasTokenAddress(deployL1ContractsValues.l1ContractAddresses.gasPortalAddress),
wallets[0],
);

aliceAddress = accounts.at(0)!.address;
bobAddress = accounts.at(1)!.address;
Expand Down
1 change: 0 additions & 1 deletion yarn-project/end-to-end/src/e2e_fees.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ describe('e2e_fees', () => {
let bananaPrivateBalances: BalancesFn;

beforeAll(async () => {
process.env.PXE_URL = '';
e2eContext = await setup(3);

const { accounts, logger, aztecNode, pxe, deployL1ContractsValues, wallets } = e2eContext;
Expand Down
Loading

0 comments on commit 640c89a

Please sign in to comment.