diff --git a/.github/workflows/sepolia-deploy.yml b/.github/workflows/sepolia-deploy.yml index 957820a21106..d908f89827de 100644 --- a/.github/workflows/sepolia-deploy.yml +++ b/.github/workflows/sepolia-deploy.yml @@ -20,6 +20,7 @@ env: TF_VAR_L1_CHAIN_ID: 11155111 TF_VAR_ETHEREUM_HOST: https://sepolia.infura.io/v3/${{ secrets.SEPOLIA_API_KEY }} TF_VAR_PROVING_ENABLED: false + TF_VAR_BOT_TOKEN_CONTRACT: EasyPrivateTokenContract TF_VAR_API_KEY: ${{ secrets.SEPOLIANET_API_KEY }} # Node / Sequencer TF_VAR_BOOTSTRAP_NODES: "" diff --git a/yarn-project/aztec-node/package.json b/yarn-project/aztec-node/package.json index 9e9d0c00048e..f2bbd2972dec 100644 --- a/yarn-project/aztec-node/package.json +++ b/yarn-project/aztec-node/package.json @@ -14,6 +14,7 @@ }, "scripts": { "start": "node --no-warnings ./dest/bin", + "start:debug": "node --no-warnings --inspect ./dest/bin", "build": "yarn clean && tsc -b", "build:dev": "tsc -b --watch", "clean": "rm -rf ./dest .tsbuildinfo", diff --git a/yarn-project/aztec.js/src/index.ts b/yarn-project/aztec.js/src/index.ts index 84e5a7d6c14f..3c012b20f287 100644 --- a/yarn-project/aztec.js/src/index.ts +++ b/yarn-project/aztec.js/src/index.ts @@ -29,6 +29,7 @@ export { type ContractStorageLayout, DefaultWaitOpts, DeployMethod, + DeployOptions, DeploySentTx, type SendMethodOptions, SentTx, diff --git a/yarn-project/aztec/terraform/bot/main.tf b/yarn-project/aztec/terraform/bot/main.tf index f49e44b334ed..45a6321e13d3 100644 --- a/yarn-project/aztec/terraform/bot/main.tf +++ b/yarn-project/aztec/terraform/bot/main.tf @@ -176,7 +176,8 @@ resource "aws_ecs_task_definition" "aztec-bot" { { name = "BOT_SKIP_PUBLIC_SIMULATION", value = tostring(var.BOT_SKIP_PUBLIC_SIMULATION) }, { name = "BOT_L2_GAS_LIMIT", value = var.BOT_L2_GAS_LIMIT }, { name = "BOT_DA_GAS_LIMIT", value = var.BOT_DA_GAS_LIMIT }, - { name = "LOG_JSON", value = "1" } + { name = "LOG_JSON", value = "1" }, + { name = "BOT_TOKEN_CONTRACT", value = var.BOT_TOKEN_CONTRACT } ] logConfiguration = { logDriver = "awslogs" diff --git a/yarn-project/aztec/terraform/bot/variables.tf b/yarn-project/aztec/terraform/bot/variables.tf index 679dd2187ab7..5d7b022fe58f 100644 --- a/yarn-project/aztec/terraform/bot/variables.tf +++ b/yarn-project/aztec/terraform/bot/variables.tf @@ -80,4 +80,8 @@ variable "BOT_DA_GAS_LIMIT" { type = string } +variable "BOT_TOKEN_CONTRACT" { + type = string + default = "TokenContract" +} diff --git a/yarn-project/bot/src/bot.ts b/yarn-project/bot/src/bot.ts index 05ba6df85f86..9e9efcb91796 100644 --- a/yarn-project/bot/src/bot.ts +++ b/yarn-project/bot/src/bot.ts @@ -10,11 +10,11 @@ import { import { type AztecNode, type FunctionCall, type PXE } from '@aztec/circuit-types'; import { Gas, GasSettings } from '@aztec/circuits.js'; import { times } from '@aztec/foundation/collection'; -import { type TokenContract } from '@aztec/noir-contracts.js'; +import { type EasyPrivateTokenContract, type TokenContract } from '@aztec/noir-contracts.js'; import { type BotConfig } from './config.js'; import { BotFactory } from './factory.js'; -import { getBalances } from './utils.js'; +import { getBalances, getPrivateBalance, isStandardTokenContract } from './utils.js'; const TRANSFER_AMOUNT = 1; @@ -23,7 +23,7 @@ export class Bot { protected constructor( public readonly wallet: Wallet, - public readonly token: TokenContract, + public readonly token: TokenContract | EasyPrivateTokenContract, public readonly recipient: AztecAddress, public config: BotConfig, ) {} @@ -50,12 +50,21 @@ export class Bot { logCtx, ); - const calls: FunctionCall[] = [ - ...times(privateTransfersPerTx, () => token.methods.transfer(recipient, TRANSFER_AMOUNT).request()), - ...times(publicTransfersPerTx, () => - token.methods.transfer_public(sender, recipient, TRANSFER_AMOUNT, 0).request(), - ), - ]; + const calls: FunctionCall[] = []; + if (isStandardTokenContract(token)) { + calls.push(...times(privateTransfersPerTx, () => token.methods.transfer(recipient, TRANSFER_AMOUNT).request())); + calls.push( + ...times(publicTransfersPerTx, () => + token.methods.transfer_public(sender, recipient, TRANSFER_AMOUNT, 0).request(), + ), + ); + } else { + calls.push( + ...times(privateTransfersPerTx, () => + token.methods.transfer(TRANSFER_AMOUNT, sender, recipient, sender).request(), + ), + ); + } const opts = this.getSendMethodOpts(); const batch = new BatchCall(wallet, calls); @@ -88,10 +97,23 @@ export class Bot { } public async getBalances() { - return { - sender: await getBalances(this.token, this.wallet.getAddress()), - recipient: await getBalances(this.token, this.recipient), - }; + if (isStandardTokenContract(this.token)) { + return { + sender: await getBalances(this.token, this.wallet.getAddress()), + recipient: await getBalances(this.token, this.recipient), + }; + } else { + return { + sender: { + privateBalance: await getPrivateBalance(this.token, this.wallet.getAddress()), + publicBalance: 0n, + }, + recipient: { + privateBalance: await getPrivateBalance(this.token, this.recipient), + publicBalance: 0n, + }, + }; + } } private getSendMethodOpts(): SendMethodOptions { diff --git a/yarn-project/bot/src/config.ts b/yarn-project/bot/src/config.ts index 7d896ac5023a..b4cdd268d04f 100644 --- a/yarn-project/bot/src/config.ts +++ b/yarn-project/bot/src/config.ts @@ -11,6 +11,11 @@ import { const botFollowChain = ['NONE', 'PENDING', 'PROVEN'] as const; type BotFollowChain = (typeof botFollowChain)[number]; +export enum SupportedTokenContracts { + TokenContract = 'TokenContract', + EasyPrivateTokenContract = 'EasyPrivateTokenContract', +} + export type BotConfig = { /** The URL to the Aztec node to check for tx pool status. */ nodeUrl: string | undefined; @@ -46,6 +51,8 @@ export type BotConfig = { l2GasLimit: number | undefined; /** DA gas limit for the tx (empty to have the bot trigger an estimate gas). */ daGasLimit: number | undefined; + /** Token contract to use */ + contract: SupportedTokenContracts; }; export const botConfigMappings: ConfigMappingsType = { @@ -142,6 +149,21 @@ export const botConfigMappings: ConfigMappingsType = { description: 'DA gas limit for the tx (empty to have the bot trigger an estimate gas).', ...optionalNumberConfigHelper(), }, + contract: { + env: 'BOT_TOKEN_CONTRACT', + description: 'Token contract to use', + defaultValue: SupportedTokenContracts.TokenContract, + parseEnv(val) { + if (!Object.values(SupportedTokenContracts).includes(val as any)) { + throw new Error( + `Invalid value for BOT_TOKEN_CONTRACT: ${val}. Valid values: ${Object.values(SupportedTokenContracts).join( + ', ', + )}`, + ); + } + return val as SupportedTokenContracts; + }, + }, }; export function getBotConfigFromEnv(): BotConfig { diff --git a/yarn-project/bot/src/factory.ts b/yarn-project/bot/src/factory.ts index e909590c268f..541acc975da7 100644 --- a/yarn-project/bot/src/factory.ts +++ b/yarn-project/bot/src/factory.ts @@ -1,11 +1,19 @@ import { getSchnorrAccount } from '@aztec/accounts/schnorr'; -import { type AccountWallet, BatchCall, createDebugLogger, createPXEClient } from '@aztec/aztec.js'; +import { + type AccountWallet, + BatchCall, + type DeployMethod, + type DeployOptions, + createDebugLogger, + createPXEClient, +} from '@aztec/aztec.js'; import { type AztecNode, type FunctionCall, type PXE } from '@aztec/circuit-types'; import { Fr, deriveSigningKey } from '@aztec/circuits.js'; +import { EasyPrivateTokenContract } from '@aztec/noir-contracts.js'; import { TokenContract } from '@aztec/noir-contracts.js/Token'; -import { type BotConfig } from './config.js'; -import { getBalances } from './utils.js'; +import { type BotConfig, SupportedTokenContracts } from './config.js'; +import { getBalances, getPrivateBalance, isStandardTokenContract } from './utils.js'; const MINT_BALANCE = 1e12; const MIN_BALANCE = 1e3; @@ -86,9 +94,21 @@ export class BotFactory { * @param wallet - Wallet to deploy the token contract from. * @returns The TokenContract instance. */ - private async setupToken(wallet: AccountWallet): Promise { - const deploy = TokenContract.deploy(wallet, wallet.getAddress(), 'BotToken', 'BOT', 18); - const deployOpts = { contractAddressSalt: this.config.tokenSalt, universalDeploy: true }; + private async setupToken(wallet: AccountWallet): Promise { + let deploy: DeployMethod; + const deployOpts: DeployOptions = { contractAddressSalt: this.config.tokenSalt, universalDeploy: true }; + if (this.config.contract === SupportedTokenContracts.TokenContract) { + deploy = TokenContract.deploy(wallet, wallet.getAddress(), 'BotToken', 'BOT', 18); + } else if (this.config.contract === SupportedTokenContracts.EasyPrivateTokenContract) { + deploy = EasyPrivateTokenContract.deploy(wallet, MINT_BALANCE, wallet.getAddress(), wallet.getAddress()); + deployOpts.skipPublicDeployment = true; + deployOpts.skipClassRegistration = true; + deployOpts.skipInitialization = false; + deployOpts.skipPublicSimulation = true; + } else { + throw new Error(`Unsupported token contract type: ${this.config.contract}`); + } + const address = deploy.getInstance(deployOpts).address; if (await this.pxe.isContractPubliclyDeployed(address)) { this.log.info(`Token at ${address.toString()} already deployed`); @@ -111,15 +131,29 @@ export class BotFactory { * Mints private and public tokens for the sender if their balance is below the minimum. * @param token - Token contract. */ - private async mintTokens(token: TokenContract) { + private async mintTokens(token: TokenContract | EasyPrivateTokenContract) { const sender = token.wallet.getAddress(); - const { privateBalance, publicBalance } = await getBalances(token, sender); + const isStandardToken = isStandardTokenContract(token); + let privateBalance = 0n; + let publicBalance = 0n; + + if (isStandardToken) { + ({ privateBalance, publicBalance } = await getBalances(token, sender)); + } else { + privateBalance = await getPrivateBalance(token, sender); + } + const calls: FunctionCall[] = []; if (privateBalance < MIN_BALANCE) { this.log.info(`Minting private tokens for ${sender.toString()}`); - calls.push(token.methods.privately_mint_private_note(MINT_BALANCE).request()); + + calls.push( + isStandardToken + ? token.methods.privately_mint_private_note(MINT_BALANCE).request() + : token.methods.mint(MINT_BALANCE, sender, sender).request(), + ); } - if (publicBalance < MIN_BALANCE) { + if (isStandardToken && publicBalance < MIN_BALANCE) { this.log.info(`Minting public tokens for ${sender.toString()}`); calls.push(token.methods.mint_public(sender, MINT_BALANCE).request()); } diff --git a/yarn-project/bot/src/index.ts b/yarn-project/bot/src/index.ts index 30278ea4d5da..43025b389563 100644 --- a/yarn-project/bot/src/index.ts +++ b/yarn-project/bot/src/index.ts @@ -1,4 +1,10 @@ export { Bot } from './bot.js'; export { BotRunner } from './runner.js'; -export { BotConfig, getBotConfigFromEnv, getBotDefaultConfig, botConfigMappings } from './config.js'; +export { + BotConfig, + getBotConfigFromEnv, + getBotDefaultConfig, + botConfigMappings, + SupportedTokenContracts, +} from './config.js'; export { createBotRunnerRpcServer } from './rpc.js'; diff --git a/yarn-project/bot/src/utils.ts b/yarn-project/bot/src/utils.ts index bb8297e143bf..4c90afa8fba8 100644 --- a/yarn-project/bot/src/utils.ts +++ b/yarn-project/bot/src/utils.ts @@ -1,4 +1,5 @@ import { type AztecAddress } from '@aztec/circuits.js'; +import { type EasyPrivateTokenContract } from '@aztec/noir-contracts.js'; import { type TokenContract } from '@aztec/noir-contracts.js/Token'; /** @@ -15,3 +16,12 @@ export async function getBalances( const publicBalance = await token.methods.balance_of_public(who).simulate(); return { privateBalance, publicBalance }; } + +export async function getPrivateBalance(token: EasyPrivateTokenContract, who: AztecAddress): Promise { + const privateBalance = await token.methods.get_balance(who).simulate(); + return privateBalance; +} + +export function isStandardTokenContract(token: TokenContract | EasyPrivateTokenContract): token is TokenContract { + return 'mint_public' in token.methods; +} diff --git a/yarn-project/end-to-end/src/e2e_bot.test.ts b/yarn-project/end-to-end/src/e2e_bot.test.ts index 1f1c26a24e23..5446f6f31c64 100644 --- a/yarn-project/end-to-end/src/e2e_bot.test.ts +++ b/yarn-project/end-to-end/src/e2e_bot.test.ts @@ -1,7 +1,7 @@ import { Fr, type PXE } from '@aztec/aztec.js'; import { Bot, type BotConfig } from '@aztec/bot'; +import { SupportedTokenContracts, getBotDefaultConfig } from '@aztec/bot'; -import { getBotDefaultConfig } from '../../bot/src/config.js'; import { setup } from './fixtures/utils.js'; describe('e2e_bot', () => { @@ -49,4 +49,20 @@ describe('e2e_bot', () => { expect(bot2.token.address.toString()).toEqual(token.address.toString()); expect(bot2.recipient.toString()).toEqual(recipient.toString()); }); + + it('sends token from the bot using EasyPrivateToken', async () => { + const easyBot = await Bot.create( + { + ...config, + contract: SupportedTokenContracts.EasyPrivateTokenContract, + }, + { pxe }, + ); + const { recipient: recipientBefore } = await easyBot.getBalances(); + + await easyBot.run(); + const { recipient: recipientAfter } = await easyBot.getBalances(); + expect(recipientAfter.privateBalance - recipientBefore.privateBalance).toEqual(1n); + expect(recipientAfter.publicBalance - recipientBefore.publicBalance).toEqual(0n); + }); }); diff --git a/yarn-project/foundation/src/config/env_var.ts b/yarn-project/foundation/src/config/env_var.ts index 5d226f958459..47167a47273a 100644 --- a/yarn-project/foundation/src/config/env_var.ts +++ b/yarn-project/foundation/src/config/env_var.ts @@ -120,6 +120,7 @@ export type EnvVar = | 'PXE_PROVER_ENABLED' | 'BOT_FOLLOW_CHAIN' | 'BOT_FLUSH_SETUP_TRANSACTIONS' + | 'BOT_TOKEN_CONTRACT' | 'VALIDATOR_PRIVATE_KEY' | 'VALIDATOR_DISABLED' | 'VALIDATOR_ATTESTATIONS_WAIT_TIMEOUT_MS' diff --git a/yarn-project/p2p/src/config.ts b/yarn-project/p2p/src/config.ts index ccbd98322bc2..d6d4d743656c 100644 --- a/yarn-project/p2p/src/config.ts +++ b/yarn-project/p2p/src/config.ts @@ -2,6 +2,7 @@ import { type ConfigMappingsType, booleanConfigHelper, getConfigFromMappings, + getDefaultConfig, numberConfigHelper, pickConfigMappings, } from '@aztec/foundation/config'; @@ -306,15 +307,7 @@ export function getP2PConfigEnvVars(): P2PConfig { } export function getP2PDefaultConfig(): P2PConfig { - const result: Partial = {}; - - for (const [key, mapping] of Object.entries(p2pConfigMappings)) { - if (mapping.defaultValue !== undefined) { - result[key as keyof P2PConfig] = mapping.defaultValue; - } - } - - return result as P2PConfig; + return getDefaultConfig(p2pConfigMappings); } /**