@@ -14,106 +14,107 @@ import {
14
14
getContract ,
15
15
} from 'viem' ;
16
16
17
- /**
18
- * A Class for testing cross chain interactions, contains common interactions
19
- * shared between cross chain tests.
20
- */
21
- abstract class PortalManager {
22
- protected constructor (
23
- /** Underlying token for portal tests. */
24
- public underlyingERC20Address : EthAddress ,
25
- /** Portal address. */
26
- public tokenPortalAddress : EthAddress ,
27
- public publicClient : PublicClient < HttpTransport , Chain > ,
28
- public walletClient : WalletClient < HttpTransport , Chain , Account > ,
29
- /** Logger. */
30
- public logger : DebugLogger ,
31
- ) { }
32
-
33
- generateClaimSecret ( ) : [ Fr , Fr ] {
34
- this . logger . debug ( "Generating a claim secret using pedersen's hash function" ) ;
35
- const secret = Fr . random ( ) ;
36
- const secretHash = computeSecretHash ( secret ) ;
37
- this . logger . info ( 'Generated claim secret: ' + secretHash . toString ( ) ) ;
38
- return [ secret , secretHash ] ;
39
- }
17
+ export enum TransferType {
18
+ PRIVATE ,
19
+ PUBLIC ,
20
+ }
21
+
22
+ export interface L2Claim {
23
+ claimSecret : Fr ;
24
+ claimAmount : Fr ;
25
+ }
26
+
27
+ function stringifyEthAddress ( address : EthAddress | Hex , name ?: string ) {
28
+ return name ? `${ name } (${ address . toString ( ) } )` : address . toString ( ) ;
29
+ }
40
30
41
- getERC20Contract ( ) : GetContractReturnType < typeof PortalERC20Abi , WalletClient < HttpTransport , Chain , Account > > {
42
- return getContract ( {
43
- address : this . underlyingERC20Address . toString ( ) ,
31
+ function generateClaimSecret ( ) : [ Fr , Fr ] {
32
+ const secret = Fr . random ( ) ;
33
+ const secretHash = computeSecretHash ( secret ) ;
34
+ return [ secret , secretHash ] ;
35
+ }
36
+
37
+ class L1TokenManager {
38
+ private contract : GetContractReturnType < typeof PortalERC20Abi , WalletClient < HttpTransport , Chain , Account > > ;
39
+
40
+ public constructor (
41
+ public readonly address : EthAddress ,
42
+ private publicClient : PublicClient < HttpTransport , Chain > ,
43
+ private walletClient : WalletClient < HttpTransport , Chain , Account > ,
44
+ private logger : DebugLogger ,
45
+ ) {
46
+ this . contract = getContract ( {
47
+ address : this . address . toString ( ) ,
44
48
abi : PortalERC20Abi ,
45
49
client : this . walletClient ,
46
50
} ) ;
47
51
}
48
52
49
- async mintTokensOnL1 ( amount : bigint ) {
50
- this . logger . info (
51
- `Minting tokens on L1 for ${ this . walletClient . account . address } in contract ${ this . underlyingERC20Address } ` ,
52
- ) ;
53
- await this . publicClient . waitForTransactionReceipt ( {
54
- hash : await this . getERC20Contract ( ) . write . mint ( [ this . walletClient . account . address , amount ] ) ,
55
- } ) ;
56
- }
57
-
58
- async getL1TokenBalance ( address : EthAddress ) {
59
- return await this . getERC20Contract ( ) . read . balanceOf ( [ address . toString ( ) ] ) ;
53
+ public async getL1TokenBalance ( address : Hex ) {
54
+ return await this . contract . read . balanceOf ( [ address ] ) ;
60
55
}
61
56
62
- protected async sendTokensToPortalPublic ( bridgeAmount : bigint , l2Address : AztecAddress , secretHash : Fr ) {
63
- this . logger . info ( `Approving erc20 tokens for the TokenPortal at ${ this . tokenPortalAddress . toString ( ) } ` ) ;
57
+ public async mint ( amount : bigint , address : Hex , addressName = '' ) {
58
+ this . logger . info ( `Minting ${ amount } tokens for ${ stringifyEthAddress ( address , addressName ) } ` ) ;
64
59
await this . publicClient . waitForTransactionReceipt ( {
65
- hash : await this . getERC20Contract ( ) . write . approve ( [ this . tokenPortalAddress . toString ( ) , bridgeAmount ] ) ,
60
+ hash : await this . contract . write . mint ( [ address , amount ] ) ,
66
61
} ) ;
67
-
68
- const messageHash = await this . bridgeTokens ( l2Address , bridgeAmount , secretHash ) ;
69
- return Fr . fromString ( messageHash ) ;
70
62
}
71
63
72
- protected abstract bridgeTokens ( to : AztecAddress , amount : bigint , secretHash : Fr ) : Promise < Hex > ;
73
-
74
- async prepareTokensOnL1 ( l1TokenBalance : bigint , bridgeAmount : bigint , owner : AztecAddress , mint = true ) {
75
- const [ secret , secretHash ] = this . generateClaimSecret ( ) ;
76
-
77
- // Mint tokens on L1
78
- if ( mint ) {
79
- await this . mintTokensOnL1 ( l1TokenBalance ) ;
80
- }
81
-
82
- // Deposit tokens to the TokenPortal
83
- const msgHash = await this . sendTokensToPortalPublic ( bridgeAmount , owner , secretHash ) ;
84
-
85
- return { secret, msgHash, secretHash } ;
64
+ public async approve ( amount : bigint , address : Hex , addressName = '' ) {
65
+ this . logger . info ( `Minting ${ amount } tokens for ${ stringifyEthAddress ( address , addressName ) } ` ) ;
66
+ await this . publicClient . waitForTransactionReceipt ( {
67
+ hash : await this . contract . write . approve ( [ address , amount ] ) ,
68
+ } ) ;
86
69
}
87
70
}
88
71
89
- export class FeeJuicePortalManager extends PortalManager {
90
- async bridgeTokens ( to : AztecAddress , amount : bigint , secretHash : Fr ) : Promise < Hex > {
91
- const portal = getContract ( {
92
- address : this . tokenPortalAddress . toString ( ) ,
72
+ export class FeeJuicePortalManager {
73
+ tokenManager : L1TokenManager ;
74
+ contract : GetContractReturnType < typeof FeeJuicePortalAbi , WalletClient < HttpTransport , Chain , Account > > ;
75
+
76
+ constructor (
77
+ portalAddress : EthAddress ,
78
+ tokenAddress : EthAddress ,
79
+ private publicClient : PublicClient < HttpTransport , Chain > ,
80
+ private walletClient : WalletClient < HttpTransport , Chain , Account > ,
81
+ /** Logger. */
82
+ private logger : DebugLogger ,
83
+ ) {
84
+ this . tokenManager = new L1TokenManager ( tokenAddress , publicClient , walletClient , logger ) ;
85
+ this . contract = getContract ( {
86
+ address : portalAddress . toString ( ) ,
93
87
abi : FeeJuicePortalAbi ,
94
88
client : this . walletClient ,
95
89
} ) ;
90
+ }
96
91
97
- this . logger . info (
98
- `Simulating token portal deposit configured for token ${ await portal . read . l2TokenAddress ( ) } with registry ${ await portal . read . registry ( ) } to retrieve message hash` ,
99
- ) ;
92
+ public async bridgeTokensPublic ( to : AztecAddress , amount : bigint , mint = false ) : Promise < L2Claim > {
93
+ const [ claimSecret , claimSecretHash ] = generateClaimSecret ( ) ;
94
+ if ( mint ) {
95
+ await this . tokenManager . mint ( amount , this . walletClient . account . address ) ;
96
+ }
100
97
101
- const args = [ to . toString ( ) , amount , secretHash . toString ( ) ] as const ;
102
- const { result : messageHash } = await portal . simulate . depositToAztecPublic ( args ) ;
103
- this . logger . info ( 'Sending messages to L1 portal to be consumed publicly' ) ;
98
+ await this . tokenManager . approve ( amount , this . contract . address , 'FeeJuice Portal' ) ;
104
99
100
+ this . logger . info ( 'Sending L1 Fee Juice to L2 to be claimed publicly' ) ;
101
+ const args = [ to . toString ( ) , amount , claimSecretHash . toString ( ) ] as const ;
105
102
await this . publicClient . waitForTransactionReceipt ( {
106
- hash : await portal . write . depositToAztecPublic ( args ) ,
103
+ hash : await this . contract . write . depositToAztecPublic ( args ) ,
107
104
} ) ;
108
- return messageHash ;
105
+
106
+ return {
107
+ claimAmount : new Fr ( amount ) ,
108
+ claimSecret,
109
+ } ;
109
110
}
110
111
111
- public static async create (
112
+ public static async new (
112
113
pxe : PXE ,
113
114
publicClient : PublicClient < HttpTransport , Chain > ,
114
115
walletClient : WalletClient < HttpTransport , Chain , Account > ,
115
116
logger : DebugLogger ,
116
- ) : Promise < PortalManager > {
117
+ ) : Promise < FeeJuicePortalManager > {
117
118
const {
118
119
l1ContractAddresses : { feeJuiceAddress, feeJuicePortalAddress } ,
119
120
} = await pxe . getNodeInfo ( ) ;
@@ -122,39 +123,66 @@ export class FeeJuicePortalManager extends PortalManager {
122
123
throw new Error ( 'Portal or token not deployed on L1' ) ;
123
124
}
124
125
125
- return new FeeJuicePortalManager ( feeJuiceAddress , feeJuicePortalAddress , publicClient , walletClient , logger ) ;
126
+ return new FeeJuicePortalManager ( feeJuicePortalAddress , feeJuicePortalAddress , publicClient , walletClient , logger ) ;
126
127
}
127
128
}
128
129
129
- export class ERC20PortalManager extends PortalManager {
130
- async bridgeTokens ( to : AztecAddress , amount : bigint , secretHash : Fr ) : Promise < Hex > {
131
- const portal = getContract ( {
132
- address : this . tokenPortalAddress . toString ( ) ,
130
+ export class L1PortalManager {
131
+ contract : GetContractReturnType < typeof TokenPortalAbi , WalletClient < HttpTransport , Chain , Account > > ;
132
+ private tokenManager : L1TokenManager ;
133
+
134
+ constructor (
135
+ portalAddress : EthAddress ,
136
+ tokenAddress : EthAddress ,
137
+ private publicClient : PublicClient < HttpTransport , Chain > ,
138
+ private walletClient : WalletClient < HttpTransport , Chain , Account > ,
139
+ private logger : DebugLogger ,
140
+ ) {
141
+ this . tokenManager = new L1TokenManager ( tokenAddress , publicClient , walletClient , logger ) ;
142
+ this . contract = getContract ( {
143
+ address : portalAddress . toString ( ) ,
133
144
abi : TokenPortalAbi ,
134
145
client : this . walletClient ,
135
146
} ) ;
147
+ }
136
148
137
- this . logger . info (
138
- `Simulating token portal deposit configured for token ${ await portal . read . l2Bridge ( ) } with registry ${ await portal . read . registry ( ) } to retrieve message hash` ,
139
- ) ;
140
-
141
- const args = [ to . toString ( ) , amount , secretHash . toString ( ) ] as const ;
142
- const { result : messageHash } = await portal . simulate . depositToAztecPublic ( args ) ;
143
- this . logger . info ( 'Sending messages to L1 portal to be consumed publicly' ) ;
149
+ public bridgeTokensPublic ( to : AztecAddress , amount : bigint , mint = false ) : Promise < L2Claim > {
150
+ return this . bridgeTokens ( to , amount , mint , /* privateTransfer */ false ) ;
151
+ }
144
152
145
- await this . publicClient . waitForTransactionReceipt ( {
146
- hash : await portal . write . depositToAztecPublic ( args ) ,
147
- } ) ;
148
- return messageHash ;
153
+ public bridgeTokensPrivate ( to : AztecAddress , amount : bigint , mint = false ) : Promise < L2Claim > {
154
+ return this . bridgeTokens ( to , amount , mint , /* privateTransfer */ true ) ;
149
155
}
150
156
151
- public static create (
152
- tokenAddress : EthAddress ,
153
- portalAddress : EthAddress ,
154
- publicClient : PublicClient < HttpTransport , Chain > ,
155
- walletClient : WalletClient < HttpTransport , Chain , Account > ,
156
- logger : DebugLogger ,
157
- ) : Promise < ERC20PortalManager > {
158
- return Promise . resolve ( new ERC20PortalManager ( tokenAddress , portalAddress , publicClient , walletClient , logger ) ) ;
157
+ private async bridgeTokens (
158
+ to : AztecAddress ,
159
+ amount : bigint ,
160
+ mint : boolean ,
161
+ privateTransfer : boolean ,
162
+ ) : Promise < L2Claim > {
163
+ const [ claimSecret , claimSecretHash ] = generateClaimSecret ( ) ;
164
+
165
+ if ( mint ) {
166
+ await this . tokenManager . mint ( amount , this . walletClient . account . address ) ;
167
+ }
168
+
169
+ await this . tokenManager . approve ( amount , this . contract . address , 'TokenPortal' ) ;
170
+
171
+ if ( privateTransfer ) {
172
+ this . logger . info ( 'Sending L1 tokens to L2 to be claimed privately' ) ;
173
+ await this . publicClient . waitForTransactionReceipt ( {
174
+ hash : await this . contract . write . depositToAztecPrivate ( [ Fr . ZERO . toString ( ) , amount , claimSecretHash . toString ( ) ] ) ,
175
+ } ) ;
176
+ } else {
177
+ this . logger . info ( 'Sending L1 tokens to L2 to be claimed publicly' ) ;
178
+ await this . publicClient . waitForTransactionReceipt ( {
179
+ hash : await this . contract . write . depositToAztecPublic ( [ to . toString ( ) , amount , claimSecretHash . toString ( ) ] ) ,
180
+ } ) ;
181
+ }
182
+
183
+ return {
184
+ claimAmount : new Fr ( amount ) ,
185
+ claimSecret,
186
+ } ;
159
187
}
160
188
}
0 commit comments