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  }