github.com/onflow/flow-go@v0.33.17/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 pub contract WrappedToken { 80 pub resource WrappedVault { 81 pub var vault: @FungibleToken.Vault 82 83 init(v: @FungibleToken.Vault) { 84 self.vault <- v 85 } 86 destroy() { 87 destroy self.vault 88 } 89 } 90 pub fun CreateWrappedVault(inp: @FungibleToken.Vault): @WrappedToken.WrappedVault { 91 return <-create WrappedVault(v :<- inp) 92 } 93 }`, sc.FungibleToken.Address.Hex()) 94 95 deployingTestContractScript := []byte(fmt.Sprintf(` 96 transaction { 97 prepare(signer: AuthAccount) { 98 signer.contracts.add(name: "%s", code: "%s".decodeHex()) 99 } 100 } 101 `, "WrappedToken", hex.EncodeToString([]byte(testContract)))) 102 103 txBody := flow.NewTransactionBody(). 104 SetScript(deployingTestContractScript). 105 AddAuthorizer(chain.ServiceAddress()) 106 107 tx := fvm.Transaction(txBody, 0) 108 snapshot, output, err := vm.Run(ctx, tx, view) 109 require.NoError(t, err) 110 require.NoError(t, output.Err) 111 112 err = view.Merge(snapshot) 113 require.NoError(t, err) 114 115 wrapTokenScript := []byte(fmt.Sprintf( 116 ` 117 import FungibleToken from 0x%s 118 import FlowToken from 0x%s 119 import WrappedToken from 0x%s 120 121 transaction(amount: UFix64) { 122 prepare(signer: AuthAccount) { 123 let vaultRef = signer.borrow<&FlowToken.Vault>(from: /storage/flowTokenVault) 124 ?? panic("Could not borrow reference to the owner's Vault!") 125 126 let sentVault <- vaultRef.withdraw(amount: amount) 127 let wrappedFlow <- WrappedToken.CreateWrappedVault(inp :<- sentVault) 128 signer.save(<-wrappedFlow, to: /storage/wrappedToken) 129 } 130 }`, 131 sc.FungibleToken.Address.Hex(), 132 sc.FlowToken.Address.Hex(), 133 sc.FlowServiceAccount.Address.Hex(), 134 )) 135 136 txBody = flow.NewTransactionBody(). 137 SetScript(wrapTokenScript). 138 AddArgument(jsoncdc.MustEncode(cadence.UFix64(105))). 139 AddAuthorizer(chain.ServiceAddress()) 140 141 tx = fvm.Transaction(txBody, 0) 142 snapshot, output, err = vm.Run(ctx, tx, view) 143 require.NoError(t, err) 144 require.NoError(t, output.Err) 145 146 err = view.Merge(snapshot) 147 require.NoError(t, err) 148 149 dir := t.TempDir() 150 log := zerolog.Nop() 151 reporterFactory := reporters.NewReportFileWriterFactory(dir, log) 152 153 br := reporters.NewFungibleTokenTracker(log, reporterFactory, chain, []string{reporters.FlowTokenTypeID(chain)}) 154 err = br.Report( 155 EntriesToPayloads(view.Finalize().UpdatedRegisters()), 156 ledger.State{}) 157 require.NoError(t, err) 158 159 data, err := os.ReadFile(reporterFactory.Filename(reporters.FungibleTokenTrackerReportPrefix)) 160 require.NoError(t, err) 161 162 // wrappedToken 163 require.True(t, strings.Contains(string(data), `{"path":"storage/wrappedToken/vault","address":"8c5303eaa26202d6","balance":105,"type_id":"A.7e60df042a9c0868.FlowToken.Vault"}`)) 164 // flowTokenVaults 165 require.True(t, strings.Contains(string(data), `{"path":"storage/flowTokenVault","address":"8c5303eaa26202d6","balance":99999999999699895,"type_id":"A.7e60df042a9c0868.FlowToken.Vault"}`)) 166 require.True(t, strings.Contains(string(data), `{"path":"storage/flowTokenVault","address":"9a0766d93b6608b7","balance":100000,"type_id":"A.7e60df042a9c0868.FlowToken.Vault"}`)) 167 require.True(t, strings.Contains(string(data), `{"path":"storage/flowTokenVault","address":"7e60df042a9c0868","balance":100000,"type_id":"A.7e60df042a9c0868.FlowToken.Vault"}`)) 168 require.True(t, strings.Contains(string(data), `{"path":"storage/flowTokenVault","address":"912d5440f7e3769e","balance":100000,"type_id":"A.7e60df042a9c0868.FlowToken.Vault"}`)) 169 170 // do not remove this line, see https://github.com/onflow/flow-go/pull/2237 171 t.Log("success") 172 }