1+ import { AccountWallet , CompleteAddress , createLogger , FeeJuicePaymentMethodWithClaim , Fr , L1FeeJuicePortalManager , PXE , waitForPXE , createPXEClient , Logger , FeeJuicePaymentMethod , PrivateFeePaymentMethod , PublicFeePaymentMethod } from "@aztec/aztec.js" ;
2+ import { getInitialTestAccountsWallets } from "@aztec/accounts/testing" ;
3+ import {
4+ createPublicClient ,
5+ createWalletClient ,
6+ http ,
7+ } from 'viem' ;
8+ import { getSchnorrAccount } from '@aztec/accounts/schnorr' ;
9+ import { deriveSigningKey } from '@aztec/stdlib/keys' ;
10+ import { foundry } from 'viem/chains'
11+ import { mnemonicToAccount } from 'viem/accounts' ;
12+ import { FeeJuiceContract } from "@aztec/noir-contracts.js/FeeJuice" ;
13+ import { FPCContract } from "@aztec/noir-contracts.js/FPC" ;
14+ import { EasyPrivateVotingContract } from "../src/artifacts/EasyPrivateVoting.js"
15+ import { TokenContract } from "@aztec/noir-contracts.js/Token" ;
16+ // TODO: replace with import from aztec.js when published
17+ import { SponsoredFeePaymentMethod } from '../src/utils/sponsored_fee_payment_method.js'
18+
19+ const setupSandbox = async ( ) => {
20+ const { PXE_URL = 'http://localhost:8080' } = process . env ;
21+ const pxe = await createPXEClient ( PXE_URL ) ;
22+ await waitForPXE ( pxe ) ;
23+ return pxe ;
24+ } ;
25+
26+ const MNEMONIC = 'test test test test test test test test test test test junk' ;
27+ const FEE_FUNDING_FOR_TESTER_ACCOUNT = BigInt ( 1_000e20 ) ;
28+
29+ let walletClient = getL1WalletClient ( foundry . rpcUrls . default . http [ 0 ] , 0 ) ;
30+ const ownerEthAddress = walletClient . account . address ;
31+
32+ const publicClient = createPublicClient ( {
33+ chain : foundry ,
34+ transport : http ( "http://127.0.0.1:8545" ) ,
35+ } ) ;
36+
37+ async function main ( ) {
38+
39+ let pxe : PXE ;
40+ let wallets : AccountWallet [ ] = [ ] ;
41+ let logger : Logger ;
42+
43+ const amount = FEE_FUNDING_FOR_TESTER_ACCOUNT ;
44+
45+ logger = createLogger ( 'aztec:aztec-starter' ) ;
46+
47+ pxe = await setupSandbox ( ) ;
48+ wallets = await getInitialTestAccountsWallets ( pxe ) ;
49+ const nodeInfo = ( await pxe . getNodeInfo ( ) )
50+ const l1ContractAddresses = nodeInfo . l1ContractAddresses ;
51+
52+ // Setup Schnorr AccountManager
53+
54+ let secretKey = Fr . random ( ) ;
55+ let salt = Fr . random ( ) ;
56+ let schnorrAccount = await getSchnorrAccount ( pxe , secretKey , deriveSigningKey ( secretKey ) , salt ) ;
57+
58+ const newWallet = await schnorrAccount . getWallet ( )
59+ const feeJuiceReceipient = schnorrAccount . getAddress ( )
60+
61+ // Setup and bridge fee asset to L2 to get fee juice
62+
63+ const feeJuicePortalManager = new L1FeeJuicePortalManager (
64+ l1ContractAddresses . feeJuicePortalAddress ,
65+ l1ContractAddresses . feeJuiceAddress ,
66+ //@ts -ignore
67+ publicClient ,
68+ walletClient ,
69+ logger ,
70+ ) ;
71+
72+ const claim = await feeJuicePortalManager . bridgeTokensPublic ( feeJuiceReceipient , amount , true ) ;
73+
74+ const feeJuice = await FeeJuiceContract . at ( nodeInfo . protocolContractAddresses . feeJuice , wallets [ 0 ] )
75+ logger . info ( `Fee Juice minted to ${ feeJuiceReceipient } on L2.` )
76+
77+ // Two arbitraty txs to make the L1 message available on L2
78+ const votingContract = await EasyPrivateVotingContract . deploy ( wallets [ 0 ] , wallets [ 0 ] . getAddress ( ) ) . send ( ) . deployed ( ) ;
79+ const bananaCoin = await TokenContract . deploy ( wallets [ 0 ] , wallets [ 0 ] . getAddress ( ) , "bananaCoin" , "BNC" , 18 ) . send ( ) . deployed ( )
80+
81+ // Claim Fee Juice & Pay Fees yourself
82+
83+ const claimAndPay = new FeeJuicePaymentMethodWithClaim ( newWallet , claim )
84+ await schnorrAccount . deploy ( { fee : { paymentMethod : claimAndPay } } ) . wait ( )
85+ logger . info ( `New account at ${ newWallet . getAddress ( ) } deployed using claimed funds for fees.` )
86+
87+ // Pay fees yourself
88+
89+ // Create a new voting contract instance, interacting from the newWallet
90+ const useFeeJuice = new FeeJuicePaymentMethod ( newWallet . getAddress ( ) )
91+ await votingContract . withWallet ( newWallet ) . methods . cast_vote ( wallets [ 0 ] . getAddress ( ) ) . send ( { fee : { paymentMethod : useFeeJuice } } ) . wait ( )
92+ logger . info ( `Vote cast from new account, paying fees via newWallet.` )
93+
94+ // Private Fee Payments via FPC
95+
96+ // Must use a Fee Paying Contract (FPC) to pay fees privately
97+ // Need to deploy an FPC to use Private Fee payment methods
98+
99+ // This uses bananaCoin as the fee paying asset that will be exchanged for fee juice
100+ const fpc = await FPCContract . deploy ( wallets [ 0 ] , bananaCoin . address , wallets [ 0 ] . getAddress ( ) ) . send ( ) . deployed ( )
101+ const fpcClaim = await feeJuicePortalManager . bridgeTokensPublic ( fpc . address , amount , true ) ;
102+ // 2 public txs to make the bridged fee juice available
103+ // Mint some bananaCoin and send to the newWallet to pay fees privately
104+ await bananaCoin . methods . mint_to_private ( wallets [ 0 ] . getAddress ( ) , newWallet . getAddress ( ) , FEE_FUNDING_FOR_TESTER_ACCOUNT ) . send ( ) . wait ( )
105+ // mint some public bananaCoin to the newWallet to pay fees publicly
106+ await bananaCoin . methods . mint_to_public ( newWallet . getAddress ( ) , FEE_FUNDING_FOR_TESTER_ACCOUNT ) . send ( ) . wait ( )
107+ const bananaBalance = await bananaCoin . withWallet ( newWallet ) . methods . balance_of_private ( newWallet . getAddress ( ) ) . simulate ( )
108+
109+ logger . info ( `BananaCoin balance of newWallet is ${ bananaBalance } ` )
110+
111+ await feeJuice . methods . claim ( fpc . address , fpcClaim . claimAmount , fpcClaim . claimSecret , fpcClaim . messageLeafIndex ) . send ( ) . wait ( )
112+
113+ logger . info ( `Fpc fee juice balance ${ await feeJuice . methods . balance_of_public ( fpc . address ) . simulate ( ) } ` )
114+
115+ const privateFee = new PrivateFeePaymentMethod ( fpc . address , newWallet )
116+ await bananaCoin . withWallet ( newWallet ) . methods . transfer_in_private ( newWallet . getAddress ( ) , wallets [ 0 ] . getAddress ( ) , 10 , 0 ) . send ( { fee : { paymentMethod : privateFee } } ) . wait ( )
117+
118+ logger . info ( `Transfer paid with fees via the FPC, privately.` )
119+
120+ // Public Fee Payments via FPC
121+
122+ const publicFee = new PublicFeePaymentMethod ( fpc . address , newWallet )
123+ await bananaCoin . withWallet ( newWallet ) . methods . transfer_in_private ( newWallet . getAddress ( ) , wallets [ 0 ] . getAddress ( ) , 10 , 0 ) . send ( { fee : { paymentMethod : publicFee } } ) . wait ( )
124+ logger . info ( `Transfer paid with fees via the FPC, publicly.` )
125+
126+ // Sponsored Fee Payment
127+
128+ // This method will only work in environments where there is a sponsored fee contract deployed
129+ const sponsoredPaymentMethod = await SponsoredFeePaymentMethod . new ( pxe ) ;
130+ await bananaCoin . withWallet ( newWallet ) . methods . transfer_in_private ( newWallet . getAddress ( ) , wallets [ 0 ] . getAddress ( ) , 10 , 0 ) . send ( { fee : { paymentMethod : sponsoredPaymentMethod } } ) . wait ( )
131+ logger . info ( `Transfer paid with fees from Sponsored FPC.` )
132+ }
133+
134+ main ( ) ;
135+
136+ // from here: https://github.com/AztecProtocol/aztec-packages/blob/ecbd59e58006533c8885a8b2fadbd9507489300c/yarn-project/end-to-end/src/fixtures/utils.ts#L534
137+ function getL1WalletClient ( rpcUrl : string , index : number ) {
138+ const hdAccount = mnemonicToAccount ( MNEMONIC , { addressIndex : index } ) ;
139+ return createWalletClient ( {
140+ account : hdAccount ,
141+ chain : foundry ,
142+ transport : http ( rpcUrl ) ,
143+ } ) ;
144+ }
0 commit comments