Skip to content

Commit

Permalink
feat: easy deployment of protocol contracts in e2e
Browse files Browse the repository at this point in the history
  • Loading branch information
alexghr committed Mar 6, 2024
1 parent c1d1865 commit 7cdaf63
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 29 deletions.
38 changes: 14 additions & 24 deletions yarn-project/end-to-end/src/e2e_dapp_subscription.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
FPCContract,
GasTokenContract,
} from '@aztec/noir-contracts.js';
import { GasTokenAddress } from '@aztec/protocol-contracts/gas-token';

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

Expand All @@ -27,7 +28,6 @@ import {
publicDeployAccounts,
setup,
} from './fixtures/utils.js';
import { GasPortalTestingHarnessFactory, IGasBridgingTestHarness } from './shared/gas_portal_test_harness.js';

jest.setTimeout(1_000_000);

Expand All @@ -48,13 +48,12 @@ describe('e2e_dapp_subscription', () => {
let gasTokenContract: GasTokenContract;
let bananaFPC: FPCContract;
let e2eContext: EndToEndContext;
let gasBridgeTestHarness: IGasBridgingTestHarness;
let gasBalances: BalancesFn;
let bananasPublicBalances: BalancesFn;
let bananasPrivateBalances: BalancesFn;

const SUBSCRIPTION_AMOUNT = 100n;
const BRIDGED_GAS_BALANCE = 1000n;
const INITIAL_GAS_BALANCE = 1000n;
const PUBLICLY_MINTED_BANANAS = 500n;
const PRIVATELY_MINTED_BANANAS = 600n;

Expand All @@ -64,25 +63,18 @@ describe('e2e_dapp_subscription', () => {

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

const { wallets, accounts, aztecNode } = e2eContext;

const { wallets, accounts, aztecNode, deployL1ContractsValues, logger, pxe } = e2eContext;
// this should be a SignerlessWallet but that can't call public functions directly
gasTokenContract = await GasTokenContract.at(GasTokenAddress, wallets[0]);

aliceAddress = accounts.at(0)!.address;
bobAddress = accounts.at(1)!.address;
sequencerAddress = accounts.at(2)!.address;

gasBridgeTestHarness = await GasPortalTestingHarnessFactory.create({
pxeService: pxe,
publicClient: deployL1ContractsValues.publicClient,
walletClient: deployL1ContractsValues.walletClient,
wallet: wallets[0],
logger,
mockL1: true,
});

gasTokenContract = gasBridgeTestHarness.l2Token;

await aztecNode.setConfig({
feeRecipient: sequencerAddress,
});
Expand Down Expand Up @@ -113,8 +105,8 @@ describe('e2e_dapp_subscription', () => {
// she'll pay for the subscription with these
await bananaCoin.methods.privately_mint_private_note(PRIVATELY_MINTED_BANANAS).send().wait();
await bananaCoin.methods.mint_public(aliceAddress, PUBLICLY_MINTED_BANANAS).send().wait();
await gasBridgeTestHarness.bridgeFromL1ToL2(BRIDGED_GAS_BALANCE, BRIDGED_GAS_BALANCE, subscriptionContract.address);
await gasBridgeTestHarness.bridgeFromL1ToL2(BRIDGED_GAS_BALANCE, BRIDGED_GAS_BALANCE, bananaFPC.address);
await gasTokenContract.methods.mint_public(subscriptionContract.address, INITIAL_GAS_BALANCE).send().wait();
await gasTokenContract.methods.mint_public(bananaFPC.address, INITIAL_GAS_BALANCE).send().wait();

gasBalances = getBalancesFn('⛽', gasTokenContract.methods.balance_of_public, e2eContext.logger);
bananasPublicBalances = getBalancesFn('Public 🍌', bananaCoin.methods.balance_of_public, e2eContext.logger);
Expand All @@ -123,10 +115,8 @@ describe('e2e_dapp_subscription', () => {
await expectMapping(
gasBalances,
[aliceAddress, sequencerAddress, subscriptionContract.address, bananaFPC.address],
[0n, 0n, BRIDGED_GAS_BALANCE, BRIDGED_GAS_BALANCE],
[0n, 0n, INITIAL_GAS_BALANCE, INITIAL_GAS_BALANCE],
);

await publicDeployAccounts(e2eContext.wallet, e2eContext.accounts);
});

it('should allow Alice to subscribe by paying privately with bananas', async () => {
Expand Down Expand Up @@ -160,7 +150,7 @@ describe('e2e_dapp_subscription', () => {
gasBalances,
// note the subscription contract hasn't paid any fees yet
[bananaFPC.address, subscriptionContract.address, sequencerAddress],
[BRIDGED_GAS_BALANCE - FEE_AMOUNT, BRIDGED_GAS_BALANCE, FEE_AMOUNT],
[INITIAL_GAS_BALANCE - FEE_AMOUNT, INITIAL_GAS_BALANCE, FEE_AMOUNT],
);
});

Expand Down Expand Up @@ -201,7 +191,7 @@ describe('e2e_dapp_subscription', () => {
await expectMapping(
gasBalances,
[subscriptionContract.address, bananaFPC.address, sequencerAddress],
[BRIDGED_GAS_BALANCE, BRIDGED_GAS_BALANCE - 2n * FEE_AMOUNT, 2n * FEE_AMOUNT],
[INITIAL_GAS_BALANCE, INITIAL_GAS_BALANCE - 2n * FEE_AMOUNT, 2n * FEE_AMOUNT],
);
});

Expand All @@ -219,7 +209,7 @@ describe('e2e_dapp_subscription', () => {
await expectMapping(
gasBalances,
[subscriptionContract.address, bananaFPC.address, sequencerAddress],
[BRIDGED_GAS_BALANCE - FEE_AMOUNT, BRIDGED_GAS_BALANCE - 2n * FEE_AMOUNT, FEE_AMOUNT * 3n],
[INITIAL_GAS_BALANCE - FEE_AMOUNT, INITIAL_GAS_BALANCE - 2n * FEE_AMOUNT, FEE_AMOUNT * 3n],
);
});

Expand Down
39 changes: 38 additions & 1 deletion yarn-project/end-to-end/src/fixtures/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
RollupAbi,
RollupBytecode,
} from '@aztec/l1-artifacts';
import { getCanonicalGasToken } from '@aztec/protocol-contracts/gas-token';
import { PXEService, PXEServiceConfig, createPXEService, getPXEServiceConfig } from '@aztec/pxe';
import { SequencerClient } from '@aztec/sequencer-client';

Expand Down Expand Up @@ -163,6 +164,7 @@ async function setupWithRemoteEnvironment(
config: AztecNodeConfig,
logger: DebugLogger,
numberOfAccounts: number,
deployProtocolContracts = false,
) {
// we are setting up against a remote environment, l1 contracts are already deployed
const aztecNodeUrl = getAztecUrl();
Expand Down Expand Up @@ -200,6 +202,10 @@ async function setupWithRemoteEnvironment(
const cheatCodes = CheatCodes.create(config.rpcUrl, pxeClient!);
const teardown = () => Promise.resolve();

if (deployProtocolContracts) {
await deployPublicProtocolContracts(wallets[0]);
}

return {
aztecNode,
sequencer: undefined,
Expand All @@ -221,6 +227,9 @@ type SetupOptions = {
stateLoad?: string;
/** Previously deployed contracts on L1 */
deployL1ContractsValues?: DeployL1Contracts;

/** Deploy protocol contracts */
deployProtocolContracts?: boolean;
} & Partial<AztecNodeConfig>;

/** Context for an end-to-end test as returned by the `setup` function */
Expand Down Expand Up @@ -280,7 +289,7 @@ 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);
return await setupWithRemoteEnvironment(hdAccount, config, logger, numberOfAccounts, opts.deployProtocolContracts);
}

const deployL1ContractsValues =
Expand All @@ -295,6 +304,12 @@ export async function setup(

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

if (opts.deployProtocolContracts) {
// 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]);
}

const cheatCodes = CheatCodes.create(config.rpcUrl, pxe!);

const teardown = async () => {
Expand Down Expand Up @@ -458,3 +473,25 @@ export async function expectMapping<K, V>(

expect(outputs).toEqual(expectedOutputs);
}

/**
* Deploy the protocol contracts to a running instance.
*/
export async function deployPublicProtocolContracts(deployer: Wallet) {
// "deploy" the Gas token as it contains public functions
const canonicalGasToken = getCanonicalGasToken();

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

await new BatchCall(deployer, [
(await registerContractClass(deployer, canonicalGasToken.artifact)).request(),
deployInstance(deployer, canonicalGasToken.instance, { universalDeploy: true }).request(),
])
.send()
.wait();

await expect(deployer.isContractClassPubliclyRegistered(canonicalGasToken.contractClass.id)).resolves.toBe(true);
await expect(deployer.getContractInstance(canonicalGasToken.instance.address)).resolves.toBeDefined();
}
4 changes: 3 additions & 1 deletion yarn-project/protocol-contracts/src/gas-token/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { 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);
return getCanonicalProtocolContract(GasTokenArtifact, 1, [], Point.ZERO, EthAddress.ZERO);
}

export const GasTokenAddress = getCanonicalGasToken().address;
19 changes: 16 additions & 3 deletions yarn-project/protocol-contracts/src/protocol_contract.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { AztecAddress, getContractClassFromArtifact, getContractInstanceFromDeployParams } from '@aztec/circuits.js';
import {
AztecAddress,
EthAddress,
getContractClassFromArtifact,
getContractInstanceFromDeployParams,
} from '@aztec/circuits.js';
import { ContractArtifact } from '@aztec/foundation/abi';
import { Fr } from '@aztec/foundation/fields';
import { Fr, Point } from '@aztec/foundation/fields';
import { ContractClassWithId, ContractInstanceWithAddress } from '@aztec/types/contracts';

/** Represents a canonical contract in the protocol. */
Expand All @@ -20,10 +25,18 @@ export function getCanonicalProtocolContract(
artifact: ContractArtifact,
salt: Fr | number | bigint,
initArgs: any[] = [],
publicKey: Point = Point.ZERO,
portalContractAddress = EthAddress.ZERO,
): ProtocolContract {
// TODO(@spalladino): This computes the contract class from the artifact twice.
const contractClass = getContractClassFromArtifact(artifact);
const instance = getContractInstanceFromDeployParams(artifact, initArgs, new Fr(salt));
const instance = getContractInstanceFromDeployParams(
artifact,
initArgs,
new Fr(salt),
publicKey,
portalContractAddress,
);
return {
instance,
contractClass,
Expand Down

0 comments on commit 7cdaf63

Please sign in to comment.