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  }