11import {
2+ createDepositWalletPermit2FeeAuthorization ,
23 DEPOSIT_WALLET_FACTORY_ADDRESS ,
34 deriveDepositWalletAddress ,
5+ executeDepositWalletBatch ,
46 getDepositWalletNonce ,
57 requestDepositWalletCreate ,
68 syncDepositWalletClobBalanceAllowance ,
79 toDepositWalletCalls ,
10+ waitForDepositWalletTransaction ,
811} from './depositWallet' ;
12+ import { getPermit2Nonce } from './safe/utils' ;
913import { getL2Headers } from './utils' ;
1014
1115jest . mock ( './utils' , ( ) => ( {
@@ -15,8 +19,20 @@ jest.mock('./utils', () => ({
1519 } ) ) ,
1620} ) ) ;
1721
22+ jest . mock ( './safe/utils' , ( ) => ( {
23+ getPermit2Nonce : jest . fn ( ) ,
24+ } ) ) ;
25+
1826describe ( 'deposit wallet helpers' , ( ) => {
1927 const mockFetch = jest . fn ( ) ;
28+ const ownerAddress = '0x1111111111111111111111111111111111111111' ;
29+ const depositWalletAddress = '0x2222222222222222222222222222222222222222' ;
30+ const typedSignature = `0x${ '11' . repeat ( 32 ) } ${ '22' . repeat ( 32 ) } 1b` ;
31+ const signer = {
32+ address : ownerAddress ,
33+ signTypedMessage : jest . fn ( ) . mockResolvedValue ( typedSignature ) ,
34+ signPersonalMessage : jest . fn ( ) ,
35+ } ;
2036
2137 beforeEach ( ( ) => {
2238 jest . clearAllMocks ( ) ;
@@ -28,11 +44,17 @@ describe('deposit wallet helpers', () => {
2844 POLY_API_KEY : 'apiKey' ,
2945 POLY_PASSPHRASE : 'passphrase' ,
3046 } ) ;
47+ ( getPermit2Nonce as jest . Mock ) . mockResolvedValue ( '7' ) ;
48+ signer . signTypedMessage . mockResolvedValue ( typedSignature ) ;
49+ } ) ;
50+
51+ afterEach ( ( ) => {
52+ jest . restoreAllMocks ( ) ;
3153 } ) ;
3254
3355 it ( 'derives the deterministic deposit wallet address for an owner' , ( ) => {
3456 expect (
35- deriveDepositWalletAddress ( '0x1111111111111111111111111111111111111111' ) ,
57+ deriveDepositWalletAddress ( ownerAddress ) ,
3658 ) . toBe ( '0xfAeA0f08159fcF2f573fE24E9E989B0d48f7651B' ) ;
3759 } ) ;
3860
@@ -44,7 +66,7 @@ describe('deposit wallet helpers', () => {
4466
4567 await expect (
4668 requestDepositWalletCreate ( {
47- ownerAddress : '0x1111111111111111111111111111111111111111' ,
69+ ownerAddress,
4870 } ) ,
4971 ) . resolves . toEqual ( { transactionID : 'wallet-create-1' } ) ;
5072
@@ -53,7 +75,7 @@ describe('deposit wallet helpers', () => {
5375 expect . objectContaining ( {
5476 method : 'POST' ,
5577 body : JSON . stringify ( {
56- owner : '0x1111111111111111111111111111111111111111' ,
78+ owner : ownerAddress ,
5779 factory : DEPOSIT_WALLET_FACTORY_ADDRESS ,
5880 } ) ,
5981 } ) ,
@@ -68,21 +90,125 @@ describe('deposit wallet helpers', () => {
6890
6991 await expect (
7092 getDepositWalletNonce ( {
71- ownerAddress : '0x1111111111111111111111111111111111111111' ,
93+ ownerAddress,
7294 } ) ,
7395 ) . resolves . toBe ( '42' ) ;
7496
7597 expect ( mockFetch ) . toHaveBeenCalledWith (
7698 'https://predict.api.cx.metamask.io/wallet/nonce' ,
7799 expect . objectContaining ( {
78100 body : JSON . stringify ( {
79- owner : '0x1111111111111111111111111111111111111111' ,
101+ owner : ownerAddress ,
80102 type : 'WALLET' ,
81103 } ) ,
82104 } ) ,
83105 ) ;
84106 } ) ;
85107
108+ it ( 'signs and submits deposit wallet batches with the current wallet nonce' , async ( ) => {
109+ jest . spyOn ( Date , 'now' ) . mockReturnValue ( 1_700_000_000_000 ) ;
110+ mockFetch
111+ . mockResolvedValueOnce ( {
112+ ok : true ,
113+ json : async ( ) => ( { nonce : '42' } ) ,
114+ } )
115+ . mockResolvedValueOnce ( {
116+ ok : true ,
117+ json : async ( ) => ( { transactionID : 'wallet-batch-1' } ) ,
118+ } ) ;
119+
120+ await expect (
121+ executeDepositWalletBatch ( {
122+ signer,
123+ walletAddress : depositWalletAddress ,
124+ calls : [
125+ {
126+ target : '0x3333333333333333333333333333333333333333' ,
127+ value : '0' ,
128+ data : '0xabcdef' ,
129+ } ,
130+ ] ,
131+ } ) ,
132+ ) . resolves . toEqual ( { transactionID : 'wallet-batch-1' } ) ;
133+
134+ expect ( signer . signTypedMessage ) . toHaveBeenCalledWith (
135+ expect . objectContaining ( {
136+ from : ownerAddress ,
137+ data : expect . objectContaining ( {
138+ domain : expect . objectContaining ( {
139+ name : 'DepositWallet' ,
140+ version : '1' ,
141+ verifyingContract : depositWalletAddress ,
142+ } ) ,
143+ primaryType : 'Batch' ,
144+ message : expect . objectContaining ( {
145+ wallet : depositWalletAddress ,
146+ nonce : '42' ,
147+ deadline : '1700000240' ,
148+ } ) ,
149+ } ) ,
150+ } ) ,
151+ 'V4' ,
152+ ) ;
153+ expect ( mockFetch ) . toHaveBeenLastCalledWith (
154+ 'https://predict.api.cx.metamask.io/wallet/execute' ,
155+ expect . objectContaining ( {
156+ body : JSON . stringify ( {
157+ owner : ownerAddress ,
158+ factory : DEPOSIT_WALLET_FACTORY_ADDRESS ,
159+ nonce : '42' ,
160+ signature : typedSignature ,
161+ depositWalletParams : {
162+ depositWallet : depositWalletAddress ,
163+ deadline : '1700000240' ,
164+ calls : [
165+ {
166+ target : '0x3333333333333333333333333333333333333333' ,
167+ value : '0' ,
168+ data : '0xabcdef' ,
169+ } ,
170+ ] ,
171+ } ,
172+ } ) ,
173+ } ) ,
174+ ) ;
175+ } ) ;
176+
177+ it ( 'polls deposit wallet transactions until the relayer confirms them' , async ( ) => {
178+ mockFetch
179+ . mockResolvedValueOnce ( {
180+ ok : true ,
181+ json : async ( ) => [ { state : 'STATE_PENDING' } ] ,
182+ } )
183+ . mockResolvedValueOnce ( {
184+ ok : true ,
185+ json : async ( ) => [ { state : 'STATE_CONFIRMED' } ] ,
186+ } ) ;
187+
188+ await expect (
189+ waitForDepositWalletTransaction ( {
190+ transactionID : 'wallet-batch-1' ,
191+ pollIntervalMs : 0 ,
192+ } ) ,
193+ ) . resolves . toBeUndefined ( ) ;
194+
195+ expect ( mockFetch ) . toHaveBeenCalledTimes ( 2 ) ;
196+ } ) ;
197+
198+ it ( 'rejects failed deposit wallet relayer transactions' , async ( ) => {
199+ mockFetch . mockResolvedValueOnce ( {
200+ ok : true ,
201+ json : async ( ) => ( { state : 'STATE_FAILED' } ) ,
202+ } ) ;
203+
204+ await expect (
205+ waitForDepositWalletTransaction ( {
206+ transactionID : 'wallet-batch-1' ,
207+ pollIntervalMs : 0 ,
208+ } ) ,
209+ ) . rejects . toThrow ( 'Deposit wallet transaction failed: STATE_FAILED' ) ;
210+ } ) ;
211+
86212 it ( 'maps internal Safe-style calls to deposit wallet calls' , ( ) => {
87213 expect (
88214 toDepositWalletCalls ( [
@@ -115,7 +241,7 @@ describe('deposit wallet helpers', () => {
115241 clobVersionHeader : '2' ,
116242 } ,
117243 } ,
118- signerAddress : '0x1111111111111111111111111111111111111111' ,
244+ signerAddress : ownerAddress ,
119245 apiKey : {
120246 apiKey : 'key' ,
121247 secret : 'secret' ,
@@ -129,7 +255,7 @@ describe('deposit wallet helpers', () => {
129255 requestPath :
130256 '/balance-allowance/update?asset_type=COLLATERAL&signature_type=3' ,
131257 } ,
132- address : '0x1111111111111111111111111111111111111111' ,
258+ address : ownerAddress ,
133259 apiKey : {
134260 apiKey : 'key' ,
135261 secret : 'secret' ,
@@ -143,4 +269,51 @@ describe('deposit wallet helpers', () => {
143269 } ) ,
144270 ) ;
145271 } ) ;
272+
273+ it ( 'creates Permit2 fee authorizations signed by the deposit wallet owner' , async ( ) => {
274+ jest . spyOn ( Date , 'now' ) . mockReturnValue ( 1_700_000_000_000 ) ;
275+
276+ const authorization = await createDepositWalletPermit2FeeAuthorization ( {
277+ signer,
278+ amount : 123n ,
279+ spender : '0x3333333333333333333333333333333333333333' ,
280+ tokenAddress : '0x4444444444444444444444444444444444444444' ,
281+ } ) ;
282+
283+ expect ( signer . signTypedMessage ) . toHaveBeenCalledWith (
284+ expect . objectContaining ( {
285+ from : ownerAddress ,
286+ data : expect . objectContaining ( {
287+ domain : expect . objectContaining ( { name : 'Permit2' } ) ,
288+ primaryType : 'PermitTransferFrom' ,
289+ message : {
290+ permitted : {
291+ token : '0x4444444444444444444444444444444444444444' ,
292+ amount : '123' ,
293+ } ,
294+ spender : '0x3333333333333333333333333333333333333333' ,
295+ nonce : '7' ,
296+ deadline : '1700003600' ,
297+ } ,
298+ } ) ,
299+ } ) ,
300+ 'V4' ,
301+ ) ;
302+ expect ( authorization ) . toEqual ( {
303+ type : 'safe-permit2' ,
304+ authorization : {
305+ permit : {
306+ permitted : {
307+ token : '0x4444444444444444444444444444444444444444' ,
308+ amount : '123' ,
309+ } ,
310+ spender : '0x3333333333333333333333333333333333333333' ,
311+ nonce : '7' ,
312+ deadline : '1700003600' ,
313+ } ,
314+ spender : '0x3333333333333333333333333333333333333333' ,
315+ signature : typedSignature ,
316+ } ,
317+ } ) ;
318+ } ) ;
146319} ) ;
0 commit comments