Skip to content

Commit

Permalink
refactor: add gas portal to l1 contract addresses
Browse files Browse the repository at this point in the history
  • Loading branch information
alexghr committed Mar 16, 2024
1 parent a5e7327 commit c95af69
Show file tree
Hide file tree
Showing 14 changed files with 103 additions and 43 deletions.
4 changes: 4 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,7 @@ export function getConfigEnvVars(): ArchiverConfig {
INBOX_CONTRACT_ADDRESS,
OUTBOX_CONTRACT_ADDRESS,
REGISTRY_CONTRACT_ADDRESS,
GAS_PORTAL_CONTRACT_ADDRESS,
DATA_DIRECTORY,
} = process.env;
// Populate the relevant addresses for use by the archiver.
Expand All @@ -73,6 +74,9 @@ 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,
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
1 change: 1 addition & 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,7 @@ describe('Contract Class', () => {
registryAddress: EthAddress.random(),
inboxAddress: EthAddress.random(),
outboxAddress: 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
7 changes: 4 additions & 3 deletions yarn-project/end-to-end/src/e2e_dapp_subscription.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
AccountWalletWithPrivateKey,
AztecAddress,
EthAddress,
FeePaymentMethod,
Fr,
PrivateFeePaymentMethod,
Expand All @@ -16,7 +17,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 @@ -63,13 +64,13 @@ describe('e2e_dapp_subscription', () => {

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

const { wallets, accounts, aztecNode } = 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(EthAddress.ZERO), wallets[0]);

aliceAddress = accounts.at(0)!.address;
bobAddress = accounts.at(1)!.address;
Expand Down
5 changes: 4 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 @@ -60,7 +60,10 @@ describe('e2e_fees', () => {

beforeAll(async () => {
process.env.PXE_URL = '';
e2eContext = await setup(3);
e2eContext = await setup(3, {
deployGasToken: true,
deployGasPortal: true,
});

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

Expand Down
37 changes: 26 additions & 11 deletions yarn-project/end-to-end/src/fixtures/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import { deployInstance, registerContractClass } from '@aztec/aztec.js/deploymen
import {
AvailabilityOracleAbi,
AvailabilityOracleBytecode,
GasPortalAbi,
GasPortalBytecode,
InboxAbi,
InboxBytecode,
OutboxAbi,
Expand Down Expand Up @@ -96,6 +98,7 @@ export const setupL1Contracts = async (
l1RpcUrl: string,
account: HDAccount | PrivateKeyAccount,
logger: DebugLogger,
enableGas = false,
) => {
const l1Artifacts: L1ContractArtifactsForDeployment = {
registry: {
Expand All @@ -118,6 +121,12 @@ export const setupL1Contracts = async (
contractAbi: RollupAbi,
contractBytecode: RollupBytecode,
},
gasPortal: enableGas
? {
contractAbi: GasPortalAbi,
contractBytecode: GasPortalBytecode,
}
: undefined,
};
return await deployL1Contracts(l1RpcUrl, account, foundry, logger, l1Artifacts);
};
Expand Down Expand Up @@ -183,7 +192,7 @@ async function setupWithRemoteEnvironment(
config: AztecNodeConfig,
logger: DebugLogger,
numberOfAccounts: number,
deployProtocolContracts = false,
deployGasToken = false,
) {
// we are setting up against a remote environment, l1 contracts are already deployed
const aztecNodeUrl = getAztecUrl();
Expand Down Expand Up @@ -221,8 +230,10 @@ async function setupWithRemoteEnvironment(
const cheatCodes = CheatCodes.create(config.rpcUrl, pxeClient!);
const teardown = () => Promise.resolve();

if (deployProtocolContracts) {
await deployPublicProtocolContracts(wallets[0]);
if (deployGasToken) {
// this contract might already have been deployed
// the following function is idempotent
await deployCanonicalGasToken(wallets[0]);
}

return {
Expand All @@ -247,8 +258,11 @@ type SetupOptions = {
/** Previously deployed contracts on L1 */
deployL1ContractsValues?: DeployL1Contracts;

/** Deploy protocol contracts */
deployProtocolContracts?: boolean;
/** Deploy the gas token contract on L2 */
deployGasToken?: boolean;

/** Deploy gas portal on L1. This is used to bridge assets to the gas token contract */
deployGasPortal?: boolean;
} & Partial<AztecNodeConfig>;

/** Context for an end-to-end test as returned by the `setup` function */
Expand Down Expand Up @@ -308,11 +322,11 @@ export async function setup(

if (PXE_URL) {
// we are setting up against a remote environment, l1 contracts are assumed to already be deployed
return await setupWithRemoteEnvironment(hdAccount, config, logger, numberOfAccounts, opts.deployProtocolContracts);
return await setupWithRemoteEnvironment(hdAccount, config, logger, numberOfAccounts, opts.deployGasToken);
}

const deployL1ContractsValues =
opts.deployL1ContractsValues ?? (await setupL1Contracts(config.rpcUrl, hdAccount, logger));
opts.deployL1ContractsValues ?? (await setupL1Contracts(config.rpcUrl, hdAccount, logger, opts.deployGasPortal));

config.publisherPrivateKey = `0x${publisherPrivKey!.toString('hex')}`;
config.l1Contracts = deployL1ContractsValues.l1ContractAddresses;
Expand All @@ -330,10 +344,10 @@ export async function setup(

const { pxe, accounts, wallets } = await setupPXEService(numberOfAccounts, aztecNode!, pxeOpts, logger);

if (opts.deployProtocolContracts) {
if (opts.deployGasToken) {
// this should be a neutral wallet, but the SignerlessWallet only accepts a single function call
// and this needs two: one to register the class and another to deploy the instance
await deployPublicProtocolContracts(wallets[0]);
await deployCanonicalGasToken(wallets[0]);
}

const cheatCodes = CheatCodes.create(config.rpcUrl, pxe!);
Expand Down Expand Up @@ -503,9 +517,10 @@ export async function expectMapping<K, V>(
/**
* Deploy the protocol contracts to a running instance.
*/
export async function deployPublicProtocolContracts(deployer: Wallet) {
export async function deployCanonicalGasToken(deployer: Wallet) {
// "deploy" the Gas token as it contains public functions
const canonicalGasToken = getCanonicalGasToken();
const gasPortalAddress = (await deployer.getNodeInfo()).l1ContractAddresses.gasPortalAddress;
const canonicalGasToken = getCanonicalGasToken(gasPortalAddress);

if (await deployer.isContractClassPubliclyRegistered(canonicalGasToken.contractClass.id)) {
return;
Expand Down
24 changes: 12 additions & 12 deletions yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import {
deployL1Contract,
sleep,
} from '@aztec/aztec.js';
import { GasPortalAbi, GasPortalBytecode, OutboxAbi, PortalERC20Abi, PortalERC20Bytecode } from '@aztec/l1-artifacts';
import { GasPortalAbi, OutboxAbi, PortalERC20Abi, PortalERC20Bytecode } from '@aztec/l1-artifacts';
import { GasTokenContract } from '@aztec/noir-contracts.js';
import { getCanonicalGasToken } from '@aztec/protocol-contracts/gas-token';
import { getCanonicalGasToken, getCanonicalGasTokenAddress } from '@aztec/protocol-contracts/gas-token';

import { Account, Chain, HttpTransport, PublicClient, WalletClient, getContract } from 'viem';

Expand Down Expand Up @@ -62,21 +62,21 @@ export async function deployAndInitializeTokenAndBridgeContracts(
client: walletClient,
});

// deploy the gas portal
const gasPortalAddress = await deployL1Contract(walletClient, publicClient, GasPortalAbi, GasPortalBytecode);
// the gas portal should have been deployed already
// we need to initialize it though
const gasPortalAddress = (await wallet.getNodeInfo()).l1ContractAddresses.gasPortalAddress;
if (gasPortalAddress.isZero()) {
throw new Error('Gas portal not deployed on L1');
}

const gasPortal = getContract({
address: gasPortalAddress.toString(),
abi: GasPortalAbi,
client: walletClient,
});

// deploy l2 token
const gasL2 = await GasTokenContract.deploy(wallet)
.send({
portalContract: gasPortalAddress,
contractAddressSalt: getCanonicalGasToken().instance.salt,
})
.deployed();
// the gas token on L2 should have been deployed as part of bootstrap
const gasL2 = await GasTokenContract.at(getCanonicalGasTokenAddress(gasPortalAddress), wallet);

// initialize portal
await gasPortal.write.initialize(
Expand Down Expand Up @@ -104,7 +104,7 @@ export class GasPortalTestingHarnessFactory {

const gasL2 = await GasTokenContract.deploy(wallet)
.send({
contractAddressSalt: getCanonicalGasToken().instance.salt,
contractAddressSalt: getCanonicalGasToken(EthAddress.ZERO).instance.salt,
})
.deployed();
return Promise.resolve(new MockGasBridgingTestHarness(gasL2));
Expand Down
16 changes: 16 additions & 0 deletions yarn-project/ethereum/src/deploy_l1_contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ export interface L1ContractArtifactsForDeployment {
* Rollup contract artifacts
*/
rollup: ContractArtifacts;
/**
* Gas portal contract artifacts. Optional for now as gas is not strictly enforced
*/
gasPortal?: ContractArtifacts;
}

/**
Expand Down Expand Up @@ -160,12 +164,24 @@ export const deployL1Contracts = async (
{ account },
);

let gasPortalAddress = EthAddress.ZERO;
if (contractsToDeploy.gasPortal) {
// this contract remains uninitialized because at this point we don't know the address of the gas token on L2
gasPortalAddress = await deployL1Contract(
walletClient,
publicClient,
contractsToDeploy.gasPortal.contractAbi,
contractsToDeploy.gasPortal.contractBytecode,
);
}

const l1Contracts: L1ContractAddresses = {
availabilityOracleAddress,
rollupAddress,
registryAddress,
inboxAddress,
outboxAddress,
gasPortalAddress,
};

return {
Expand Down
1 change: 1 addition & 0 deletions yarn-project/ethereum/src/l1_contract_addresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const l1ContractsNames = [
'registryAddress',
'inboxAddress',
'outboxAddress',
'gasPortalAddress',
] as const;

/**
Expand Down
6 changes: 3 additions & 3 deletions yarn-project/protocol-contracts/src/gas-token/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { EthAddress } from '@aztec/circuits.js';
import { setupCustomSnapshotSerializers } from '@aztec/foundation/testing';

import omit from 'lodash.omit';

import { GasTokenAddress, getCanonicalGasToken } from './index.js';
import { getCanonicalGasToken } from './index.js';

describe('GasToken', () => {
setupCustomSnapshotSerializers(expect);
it('returns canonical protocol contract', () => {
// if you're updating the snapshots here then you'll also have to update CANONICAL_GAS_TOKEN_ADDRESS in
// - noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr
// - noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr
const contract = getCanonicalGasToken();
const contract = getCanonicalGasToken(EthAddress.ZERO);
expect(omit(contract, ['artifact', 'contractClass'])).toMatchSnapshot();

// bytecode is very large
Expand All @@ -19,6 +20,5 @@ describe('GasToken', () => {
// this contract has public bytecode
expect(contract.contractClass.publicFunctions.map(x => omit(x, 'bytecode'))).toMatchSnapshot();
expect(contract.contractClass.packedBytecode.length).toBeGreaterThan(0);
expect(contract.address.toString()).toEqual(GasTokenAddress.toString());
});
});
10 changes: 6 additions & 4 deletions yarn-project/protocol-contracts/src/gas-token/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { EthAddress, Point } from '@aztec/circuits.js';
import { AztecAddress, EthAddress, Point } from '@aztec/circuits.js';

import { ProtocolContract, getCanonicalProtocolContract } from '../protocol_contract.js';
import { GasTokenArtifact } from './artifact.js';

/** Returns the canonical deployment of the gas token. */
export function getCanonicalGasToken(): ProtocolContract {
return getCanonicalProtocolContract(GasTokenArtifact, 1, [], Point.ZERO, EthAddress.ZERO);
export function getCanonicalGasToken(l1Bridge: EthAddress): ProtocolContract {
return getCanonicalProtocolContract(GasTokenArtifact, 1, [], Point.ZERO, l1Bridge);
}

export const GasTokenAddress = getCanonicalGasToken().address;
export function getCanonicalGasTokenAddress(l1Bridge: EthAddress): AztecAddress {
return getCanonicalGasToken(l1Bridge).address;
}
6 changes: 5 additions & 1 deletion yarn-project/pxe/src/pxe_service/create_pxe_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ export async function createPXEService(
const db = new KVPxeDatabase(await initStoreForRollup(AztecLmdbStore.open(pxeDbPath), l1Contracts.rollupAddress));

const server = new PXEService(keyStore, aztecNode, db, config, logSuffix);
await server.addContracts([getCanonicalClassRegisterer(), getCanonicalInstanceDeployer(), getCanonicalGasToken()]);
await server.addContracts([
getCanonicalClassRegisterer(),
getCanonicalInstanceDeployer(),
getCanonicalGasToken(l1Contracts.gasPortalAddress),
]);

await server.start();
return server;
Expand Down
Loading

0 comments on commit c95af69

Please sign in to comment.