github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/cmd/util/ledger/reporters/fungible_token_tracker_test.go (about) 1 package reporters_test 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 "os" 7 "strings" 8 "testing" 9 10 "github.com/onflow/cadence" 11 jsoncdc "github.com/onflow/cadence/encoding/json" 12 "github.com/rs/zerolog" 13 "github.com/stretchr/testify/require" 14 15 "github.com/onflow/flow-go/cmd/util/ledger/reporters" 16 "github.com/onflow/flow-go/fvm" 17 "github.com/onflow/flow-go/fvm/storage/state" 18 "github.com/onflow/flow-go/fvm/systemcontracts" 19 "github.com/onflow/flow-go/ledger" 20 "github.com/onflow/flow-go/model/flow" 21 "github.com/onflow/flow-go/utils/unittest" 22 ) 23 24 func registerIdToLedgerKey(id flow.RegisterID) ledger.Key { 25 keyParts := []ledger.KeyPart{ 26 ledger.NewKeyPart(0, []byte(id.Owner)), 27 ledger.NewKeyPart(2, []byte(id.Key)), 28 } 29 30 return ledger.NewKey(keyParts) 31 } 32 33 func EntriesToPayloads(updates flow.RegisterEntries) []ledger.Payload { 34 ret := make([]ledger.Payload, 0, len(updates)) 35 for _, entry := range updates { 36 key := registerIdToLedgerKey(entry.Key) 37 ret = append(ret, *ledger.NewPayload(key, ledger.Value(entry.Value))) 38 } 39 40 return ret 41 } 42 43 func TestFungibleTokenTracker(t *testing.T) { 44 45 // bootstrap ledger 46 payloads := []ledger.Payload{} 47 chain := flow.Testnet.Chain() 48 view := state.NewExecutionState( 49 reporters.NewStorageSnapshotFromPayload(payloads), 50 state.DefaultParameters()) 51 52 vm := fvm.NewVirtualMachine() 53 opts := []fvm.Option{ 54 fvm.WithChain(chain), 55 fvm.WithAuthorizationChecksEnabled(false), 56 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 57 } 58 ctx := fvm.NewContext(opts...) 59 bootstrapOptions := []fvm.BootstrapProcedureOption{ 60 fvm.WithTransactionFee(fvm.DefaultTransactionFees), 61 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 62 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 63 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 64 fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply), 65 } 66 67 snapshot, _, err := vm.Run(ctx, fvm.Bootstrap(unittest.ServiceAccountPublicKey, bootstrapOptions...), view) 68 require.NoError(t, err) 69 70 err = view.Merge(snapshot) 71 require.NoError(t, err) 72 73 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 74 75 // deploy wrapper resource 76 testContract := fmt.Sprintf(` 77 import FungibleToken from 0x%s 78 79 access(all) 80 contract WrappedToken { 81 82 access(all) 83 resource WrappedVault { 84 85 access(all) 86 var vault: @{FungibleToken.Vault} 87 88 init(v: @{FungibleToken.Vault}) { 89 self.vault <- v 90 } 91 } 92 93 access(all) 94 fun CreateWrappedVault(inp: @{FungibleToken.Vault}): @WrappedToken.WrappedVault { 95 return <-create WrappedVault(v :<- inp) 96 } 97 }`, sc.FungibleToken.Address.Hex()) 98 99 deployingTestContractScript := []byte(fmt.Sprintf(` 100 transaction { 101 prepare(signer: auth(AddContract) &Account) { 102 signer.contracts.add(name: "%s", code: "%s".decodeHex()) 103 } 104 } 105 `, "WrappedToken", hex.EncodeToString([]byte(testContract)))) 106 107 txBody := flow.NewTransactionBody(). 108 SetScript(deployingTestContractScript). 109 AddAuthorizer(chain.ServiceAddress()) 110 111 tx := fvm.Transaction(txBody, 0) 112 snapshot, output, err := vm.Run(ctx, tx, view) 113 require.NoError(t, err) 114 require.NoError(t, output.Err) 115 116 err = view.Merge(snapshot) 117 require.NoError(t, err) 118 119 wrapTokenScript := []byte(fmt.Sprintf( 120 ` 121 import FungibleToken from 0x%s 122 import FlowToken from 0x%s 123 import WrappedToken from 0x%s 124 125 transaction(amount: UFix64) { 126 prepare(signer: auth(Storage) &Account) { 127 let vaultRef = signer.storage.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault) 128 ?? panic("Could not borrow reference to the owner's Vault!") 129 130 let sentVault <- vaultRef.withdraw(amount: amount) 131 let wrappedFlow <- WrappedToken.CreateWrappedVault(inp :<- sentVault) 132 signer.storage.save(<-wrappedFlow, to: /storage/wrappedToken) 133 } 134 }`, 135 sc.FungibleToken.Address.Hex(), 136 sc.FlowToken.Address.Hex(), 137 sc.FlowServiceAccount.Address.Hex(), 138 )) 139 140 txBody = flow.NewTransactionBody(). 141 SetScript(wrapTokenScript). 142 AddArgument(jsoncdc.MustEncode(cadence.UFix64(105))). 143 AddAuthorizer(chain.ServiceAddress()) 144 145 tx = fvm.Transaction(txBody, 0) 146 snapshot, output, err = vm.Run(ctx, tx, view) 147 require.NoError(t, err) 148 require.NoError(t, output.Err) 149 150 err = view.Merge(snapshot) 151 require.NoError(t, err) 152 153 dir := t.TempDir() 154 log := zerolog.Nop() 155 reporterFactory := reporters.NewReportFileWriterFactory(dir, log) 156 157 br := reporters.NewFungibleTokenTracker(log, reporterFactory, chain, []string{reporters.FlowTokenTypeID(chain)}) 158 err = br.Report( 159 EntriesToPayloads(view.Finalize().UpdatedRegisters()), 160 ledger.State{}) 161 require.NoError(t, err) 162 163 data, err := os.ReadFile(reporterFactory.Filename(reporters.FungibleTokenTrackerReportPrefix)) 164 require.NoError(t, err) 165 166 // wrappedToken 167 require.True(t, strings.Contains(string(data), `{"path":"storage/wrappedToken/vault","address":"8c5303eaa26202d6","balance":105,"type_id":"A.7e60df042a9c0868.FlowToken.Vault"}`)) 168 // flowTokenVaults 169 require.True(t, strings.Contains(string(data), `{"path":"storage/flowTokenVault","address":"8c5303eaa26202d6","balance":99999999999599895,"type_id":"A.7e60df042a9c0868.FlowToken.Vault"}`)) 170 require.True(t, strings.Contains(string(data), `{"path":"storage/flowTokenVault","address":"9a0766d93b6608b7","balance":100000,"type_id":"A.7e60df042a9c0868.FlowToken.Vault"}`)) 171 require.True(t, strings.Contains(string(data), `{"path":"storage/flowTokenVault","address":"7e60df042a9c0868","balance":100000,"type_id":"A.7e60df042a9c0868.FlowToken.Vault"}`)) 172 require.True(t, strings.Contains(string(data), `{"path":"storage/flowTokenVault","address":"912d5440f7e3769e","balance":100000,"type_id":"A.7e60df042a9c0868.FlowToken.Vault"}`)) 173 require.True(t, strings.Contains(string(data), `{"path":"storage/flowTokenVault","address":"754aed9de6197641","balance":100000,"type_id":"A.7e60df042a9c0868.FlowToken.Vault"}`)) 174 175 // do not remove this line, see https://github.com/onflow/flow-go/pull/2237 176 t.Log("success") 177 }