11// Tests that EVM state helpers correctly set Uniswap V3 pool price and ERC4626 vault price
2- #test_fork (network : " mainnet-fork" , height : 142251136 )
2+ #test_fork (network : " mainnet-fork" , height : 143292255 )
33
44import Test
55import BlockchainHelpers
@@ -9,9 +9,7 @@ import "evm_state_helpers.cdc"
99
1010import " FlowToken"
1111
12- // Mainnet addresses (same as forked_rebalance_scenario3c_test.cdc)
1312access (all ) let whaleFlowAccount = Test .getAccount (0x92674150c9213fc9 )
14- access (all ) let coaOwnerAccount = Test .getAccount (0xe467b9dd11fa00df )
1513
1614access (all ) let factoryAddress = " 0xca6d7Bb03334bBf135902e1d919a5feccb461632"
1715access (all ) let routerAddress = " 0xeEDC6Ff75e1b10B903D9013c358e446a73d35341"
@@ -27,123 +25,132 @@ access(all) let wflowBalanceSlot = 3 as UInt256
2725access (all ) let morphoVaultTotalSupplySlot = 11 as UInt256
2826access (all ) let morphoVaultTotalAssetsSlot = 15 as UInt256
2927
30- // Bridged vault type identifiers (service account prefix may vary; use deployment)
3128access (all ) let pyusd0VaultTypeId = " A.1e4aa0b87d10b141.EVMVMBridgedToken_99af3eea856556646c98c8b9b2548fe815240750.Vault"
32- access (all ) let fusdevVaultTypeId = " A.1e4aa0b87d10b141.EVMVMBridgedToken_d069d989e2f44b70c65347d1853c0c67e10a9f8d.Vault"
3329
34- access (all )
35- fun setup () {
36- deployContractsForFork ()
37- transferFlow (signer : whaleFlowAccount , recipient : coaOwnerAccount .address , amount : 1000.0 )
30+ // Vault public paths
31+ access (all ) let pyusd0PublicPath = /public/EVMVMBridgedToken_99af3eea856556646c98c8b9b2548fe815240750Vault
32+ access (all ) let fusdevPublicPath = /public/EVMVMBridgedToken_d069d989e2f44b70c65347d1853c0c67e10a9f8dVault
3833
39- // Deposit FLOW to COA to cover bridge/gas fees for swaps (scheduled txs can consume some)
40- let depositFlowRes = Test .executeTransaction (
41- Test .Transaction (
42- code : Test .readFile (" transactions/deposit_flow_to_coa.cdc" ),
43- authorizers : [coaOwnerAccount .address ],
44- signers : [coaOwnerAccount ],
45- arguments : [5.0 ]
46- )
47- )
48- Test .expect (depositFlowRes , Test .beSucceeded ())
49- }
34+ access (all ) let univ3PoolFee : UInt64 = 3000
5035
51- access (all ) let univ3PoolFee : UInt64 = 100
36+ access (all ) var snapshot : UInt64 = 0
37+ access (all ) var testAccount = Test .createAccount ()
5238
5339access (all )
54- fun test_UniswapV3PriceSetAndSwap () {
40+ fun setup () {
41+ deployContractsForFork ()
42+ transferFlow (signer : whaleFlowAccount , recipient : testAccount .address , amount : 10000000.0 )
43+ createCOA (testAccount , fundingAmount : 5.0 )
44+
45+ // Set up a WFLOW/PYUSD0 pool at 1:1 so we can swap FLOW→PYUSD0 to fund the Cadence vault
5546 setPoolToPrice (
5647 factoryAddress : factoryAddress ,
5748 tokenAAddress : wflowAddress ,
5849 tokenBAddress : pyusd0Address ,
5950 fee : univ3PoolFee ,
60- priceTokenBPerTokenA : 2 .0 ,
51+ priceTokenBPerTokenA : 1 .0 ,
6152 tokenABalanceSlot : wflowBalanceSlot ,
6253 tokenBBalanceSlot : pyusd0BalanceSlot ,
63- signer : coaOwnerAccount
54+ signer : testAccount
6455 )
6556
66- // Set COA WFLOW balance to 100.0 for the swap
67- let flowAmount = 100.0
68- let setBalanceRes = Test .executeTransaction (
69- Test .Transaction (
70- code : Test .readFile (" transactions/set_coa_token_balance.cdc" ),
71- authorizers : [coaOwnerAccount .address ],
72- signers : [coaOwnerAccount ],
73- arguments : [wflowAddress , wflowBalanceSlot , flowAmount ]
74- )
75- )
76- Test .expect (setBalanceRes , Test .beSucceeded ())
77-
57+ // Swap FLOW→PYUSD0 to create the Cadence-side PYUSD0 vault (needed for ERC4626 deposit test)
7858 let swapRes = Test .executeTransaction (
7959 Test .Transaction (
8060 code : Test .readFile (" transactions/execute_univ3_swap.cdc" ),
81- authorizers : [coaOwnerAccount .address ],
82- signers : [coaOwnerAccount ],
83- arguments : [factoryAddress , routerAddress , quoterAddress , wflowAddress , pyusd0Address , univ3PoolFee , flowAmount ]
61+ authorizers : [testAccount .address ],
62+ signers : [testAccount ],
63+ arguments : [factoryAddress , routerAddress , quoterAddress , wflowAddress , pyusd0Address , univ3PoolFee , 11000.0 ]
8464 )
8565 )
8666 Test .expect (swapRes , Test .beSucceeded ())
8767
88- let balanceRes = Test .executeScript (
89- Test .readFile (" scripts/get_bridged_vault_balance.cdc" ),
90- [coaOwnerAccount .address , pyusd0VaultTypeId ]
91- )
92- Test .expect (balanceRes , Test .beSucceeded ())
93- let pyusd0Balance = (balanceRes .returnValue as ? UFix64 ) ?? 0.0
94- let expectedOut = flowAmount * 2.0
95- let tolerance = expectedOut * forkedPercentTolerance * 0.01
96- Test .assert (
97- equalAmounts (a : pyusd0Balance , b : expectedOut , tolerance : tolerance ),
98- message : " PYUSD0 balance \( pyusd0Balance .toString ()) not within tolerance of \( expectedOut .toString ()) "
99- )
68+ snapshot = getCurrentBlockHeight ()
69+ Test .commitBlock ()
10070}
10171
10272access (all )
103- fun test_ERC4626PriceSetAndDeposit () {
104- setVaultSharePrice (
105- vaultAddress : morphoVaultAddress ,
106- assetAddress : pyusd0Address ,
107- assetBalanceSlot : pyusd0BalanceSlot ,
108- totalSupplySlot : morphoVaultTotalSupplySlot ,
109- vaultTotalAssetsSlot : morphoVaultTotalAssetsSlot ,
110- baseAssets : 1000000000.0 ,
111- priceMultiplier : 2.0 ,
112- signer : coaOwnerAccount
113- )
73+ fun test_UniswapV3PriceSetAndSwap () {
74+ let prices = [0.5 , 1.0 , 2.0 , 3.0 , 5.0 ]
75+ let flowAmount = 10000.0
76+
77+ for price in prices {
78+ Test .reset (to : snapshot )
79+
80+ setPoolToPrice (
81+ factoryAddress : factoryAddress ,
82+ tokenAAddress : wflowAddress ,
83+ tokenBAddress : pyusd0Address ,
84+ fee : univ3PoolFee ,
85+ priceTokenBPerTokenA : UFix128 (price ),
86+ tokenABalanceSlot : wflowBalanceSlot ,
87+ tokenBBalanceSlot : pyusd0BalanceSlot ,
88+ signer : testAccount
89+ )
11490
115- // Set COA PYUSD0 balance to 1000000000.0 for the deposit
116- let fundRes = Test .executeTransaction (
117- Test .Transaction (
118- code : Test .readFile (" transactions/set_coa_token_balance.cdc" ),
119- authorizers : [coaOwnerAccount .address ],
120- signers : [coaOwnerAccount ],
121- arguments : [pyusd0Address , pyusd0BalanceSlot , 1000000000.0 ]
91+ let balanceBefore = getBalance (address : testAccount .address , vaultPublicPath : pyusd0PublicPath )!
92+
93+ let swapRes = Test .executeTransaction (
94+ Test .Transaction (
95+ code : Test .readFile (" transactions/execute_univ3_swap.cdc" ),
96+ authorizers : [testAccount .address ],
97+ signers : [testAccount ],
98+ arguments : [factoryAddress , routerAddress , quoterAddress , wflowAddress , pyusd0Address , univ3PoolFee , flowAmount ]
99+ )
122100 )
123- )
124- Test .expect (fundRes , Test .beSucceeded ())
101+ Test .expect (swapRes , Test .beSucceeded ())
125102
126- let amountIn = 1.0
127- let depositRes = Test .executeTransaction (
128- Test .Transaction (
129- code : Test .readFile (" transactions/execute_morpho_deposit.cdc" ),
130- authorizers : [coaOwnerAccount .address ],
131- signers : [coaOwnerAccount ],
132- arguments : [pyusd0VaultTypeId , morphoVaultAddress , amountIn ]
103+ let balanceAfter = getBalance (address : testAccount .address , vaultPublicPath : pyusd0PublicPath )!
104+ let swapOutput = balanceAfter - balanceBefore
105+ let expectedOut = feeAdjustedPrice (UFix128 (price ), fee : univ3PoolFee , reverse : true ) * UFix128 (flowAmount )
106+
107+ // PYUSD0 has 6 decimals, so we need to use a tolerance of 1e-6
108+ let tolerance = 0.000001
109+ Test .assert (
110+ equalAmounts (a : UFix64 (swapOutput ), b : UFix64 (expectedOut ), tolerance : tolerance ),
111+ message : " Pool price \( price ) : swap output \( swapOutput ) not within \( tolerance ) of expected \( expectedOut ) "
133112 )
134- )
135- Test .expect (depositRes , Test .beSucceeded ())
113+ log (" Pool price \( price ) : expected=\( expectedOut ) actual=\( swapOutput ) " )
114+ }
115+ }
136116
137- let balanceRes = Test .executeScript (
138- Test .readFile (" scripts/get_bridged_vault_balance.cdc" ),
139- [coaOwnerAccount .address , fusdevVaultTypeId ]
140- )
141- Test .expect (balanceRes , Test .beSucceeded ())
142- let fusdevBalance = (balanceRes .returnValue as ? UFix64 ) ?? 0.0
143- let expectedShares = 0.5
144- let tolerance = expectedShares * forkedPercentTolerance * 0.01
145- Test .assert (
146- equalAmounts (a : fusdevBalance , b : expectedShares , tolerance : tolerance ),
147- message : " FUSDEV shares \( fusdevBalance .toString ()) not within tolerance of \( expectedShares .toString ()) "
148- )
117+ access (all )
118+ fun test_ERC4626PriceSetAndDeposit () {
119+ let multipliers = [0.5 , 1.0 , 2.0 , 3.0 , 5.0 ]
120+ let amountIn = 10000.0
121+
122+ for multiplier in multipliers {
123+ Test .reset (to : snapshot )
124+
125+ setVaultSharePrice (
126+ vaultAddress : morphoVaultAddress ,
127+ assetAddress : pyusd0Address ,
128+ assetBalanceSlot : pyusd0BalanceSlot ,
129+ totalSupplySlot : morphoVaultTotalSupplySlot ,
130+ vaultTotalAssetsSlot : morphoVaultTotalAssetsSlot ,
131+ priceMultiplier : multiplier ,
132+ signer : testAccount
133+ )
134+
135+ let depositRes = Test .executeTransaction (
136+ Test .Transaction (
137+ code : Test .readFile (" transactions/execute_morpho_deposit.cdc" ),
138+ authorizers : [testAccount .address ],
139+ signers : [testAccount ],
140+ arguments : [pyusd0VaultTypeId , morphoVaultAddress , amountIn ]
141+ )
142+ )
143+ Test .expect (depositRes , Test .beSucceeded ())
144+
145+ let fusdevBalance = getBalance (address : testAccount .address , vaultPublicPath : fusdevPublicPath )!
146+ let expectedShares = amountIn / multiplier
147+
148+ // FUSDEV has 18 decimals, so we need to use a tolerance of 1e-8 (Cadence UFix64 precision)
149+ let tolerance : UFix64 = 0.00000001
150+ Test .assert (
151+ equalAmounts (a : fusdevBalance , b : expectedShares , tolerance : tolerance ),
152+ message : " Multiplier \( multiplier ) : FUSDEV shares \( fusdevBalance ) not within \( tolerance ) of expected \( expectedShares ) "
153+ )
154+ log (" Multiplier \( multiplier ) : expected=\( expectedShares ) actual=\( fusdevBalance ) " )
155+ }
149156}
0 commit comments