github.com/onflow/flow-go@v0.33.17/fvm/systemcontracts/system_contracts.go (about)

     1  // Package systemcontracts stores canonical address locations for all system
     2  // smart contracts and service events.
     3  //
     4  // System contracts are special smart contracts controlled by the service account,
     5  // a Flow account with special privileges to administer the network.
     6  //
     7  // Service events are special events defined within system contracts which
     8  // are included within execution receipts and processed by the consensus committee
     9  // to enable message-passing to the protocol state.
    10  //
    11  // For transient networks, all system contracts can be deployed to the service
    12  // account. For long-lived networks, system contracts are spread across several
    13  // accounts for historical reasons.
    14  package systemcontracts
    15  
    16  import (
    17  	"fmt"
    18  
    19  	"github.com/onflow/flow-core-contracts/lib/go/templates"
    20  
    21  	"github.com/onflow/flow-go/model/flow"
    22  )
    23  
    24  const (
    25  	// Unqualified names of system smart contracts (not including address prefix)
    26  
    27  	ContractNameEpoch               = "FlowEpoch"
    28  	ContractNameIDTableStaking      = "FlowIDTableStaking"
    29  	ContractNameClusterQC           = "FlowClusterQC"
    30  	ContractNameDKG                 = "FlowDKG"
    31  	ContractNameServiceAccount      = "FlowServiceAccount"
    32  	ContractNameFlowFees            = "FlowFees"
    33  	ContractNameStorageFees         = "FlowStorageFees"
    34  	ContractNameNodeVersionBeacon   = "NodeVersionBeacon"
    35  	ContractNameRandomBeaconHistory = "RandomBeaconHistory"
    36  	ContractNameFungibleToken       = "FungibleToken"
    37  	ContractNameFlowToken           = "FlowToken"
    38  	ContractNameNonFungibleToken    = "NonFungibleToken"
    39  	ContractNameMetadataViews       = "MetadataViews"
    40  	ContractNameViewResolver        = "ViewResolver"
    41  	ContractNameEVM                 = "EVM"
    42  
    43  	// AccountNameEVMStorage is not a contract, but a special account that is used to store EVM state
    44  	AccountNameEVMStorage = "EVMStorageAccount"
    45  
    46  	// Unqualified names of service events (not including address prefix or contract name)
    47  
    48  	EventNameEpochSetup    = "EpochSetup"
    49  	EventNameEpochCommit   = "EpochCommit"
    50  	EventNameVersionBeacon = "VersionBeacon"
    51  
    52  	//  Unqualified names of service event contract functions (not including address prefix or contract name)
    53  
    54  	ContractServiceAccountFunction_setupNewAccount                            = "setupNewAccount"
    55  	ContractServiceAccountFunction_defaultTokenBalance                        = "defaultTokenBalance"
    56  	ContractServiceAccountFunction_deductTransactionFee                       = "deductTransactionFee"
    57  	ContractServiceAccountFunction_verifyPayersBalanceForTransactionExecution = "verifyPayersBalanceForTransactionExecution"
    58  	ContractStorageFeesFunction_calculateAccountCapacity                      = "calculateAccountCapacity"
    59  	ContractStorageFeesFunction_getAccountsCapacityForTransactionStorageCheck = "getAccountsCapacityForTransactionStorageCheck"
    60  	ContractStorageFeesFunction_defaultTokenAvailableBalance                  = "defaultTokenAvailableBalance"
    61  
    62  	// Indexes of the system contracts that are deployed to an address at a specific index
    63  
    64  	FungibleTokenAccountIndex = 2
    65  	FlowTokenAccountIndex     = 3
    66  	FlowFeesAccountIndex      = 4
    67  	EVMStorageAccountIndex    = 5
    68  )
    69  
    70  // Well-known addresses for system contracts on long-running networks.
    71  // For now, all system contracts tracked by this package are deployed to the same
    72  // address (per chain) as the staking contract.
    73  //
    74  // Ref: https://docs.onflow.org/core-contracts/staking-contract-reference/
    75  var (
    76  	// stakingContractAddressMainnet is the address of the FlowIDTableStaking contract on Mainnet
    77  	stakingContractAddressMainnet = flow.HexToAddress("8624b52f9ddcd04a")
    78  	// stakingContractAddressTestnet is the address of the FlowIDTableStaking contract on Testnet
    79  	stakingContractAddressTestnet = flow.HexToAddress("9eca2b38b18b5dfe")
    80  
    81  	// nftTokenAddressTestnet is the address of the NonFungibleToken contract on Testnet
    82  	nftTokenAddressMainnet = flow.HexToAddress("1d7e57aa55817448")
    83  	// nftTokenAddressTestnet is the address of the NonFungibleToken contract on Testnet
    84  	nftTokenAddressTestnet = flow.HexToAddress("631e88ae7f1d7c20")
    85  
    86  	// evmStorageAddressTestnet is the address of the EVM state storage contract on Testnet
    87  	evmStorageAddressTestnet = flow.HexToAddress("1a54ed2be7552821")
    88  	// evmStorageAddressMainnet is the address of the EVM state storage contract on Mainnet
    89  	evmStorageAddressMainnet = flow.HexToAddress("d421a63faae318f9")
    90  )
    91  
    92  // SystemContract represents a system contract on a particular chain.
    93  type SystemContract struct {
    94  	Address flow.Address
    95  	Name    string
    96  }
    97  
    98  // SystemAccount represents an address used by the system.
    99  type SystemAccount struct {
   100  	Address flow.Address
   101  	Name    string
   102  }
   103  
   104  // ServiceEvent represents a service event on a particular chain.
   105  type ServiceEvent struct {
   106  	Address      flow.Address
   107  	ContractName string
   108  	Name         string
   109  }
   110  
   111  // QualifiedIdentifier returns the Cadence qualified identifier of the service
   112  // event, which includes the contract name and the event type name.
   113  func (se ServiceEvent) QualifiedIdentifier() string {
   114  	return fmt.Sprintf("%s.%s", se.ContractName, se.Name)
   115  }
   116  
   117  // EventType returns the full event type identifier, including the address, the
   118  // contract name, and the event type name.
   119  func (se ServiceEvent) EventType() flow.EventType {
   120  	return flow.EventType(fmt.Sprintf("A.%s.%s.%s", se.Address, se.ContractName, se.Name))
   121  }
   122  
   123  // SystemContracts is a container for all system contracts on a particular chain.
   124  type SystemContracts struct {
   125  	// epoch related contracts
   126  	Epoch          SystemContract
   127  	IDTableStaking SystemContract
   128  	ClusterQC      SystemContract
   129  	DKG            SystemContract
   130  
   131  	// service account related contracts
   132  	FlowServiceAccount  SystemContract
   133  	NodeVersionBeacon   SystemContract
   134  	RandomBeaconHistory SystemContract
   135  	FlowStorageFees     SystemContract
   136  
   137  	// token related contracts
   138  	FlowFees      SystemContract
   139  	FlowToken     SystemContract
   140  	FungibleToken SystemContract
   141  
   142  	// NFT related contracts
   143  	NonFungibleToken SystemContract
   144  	MetadataViews    SystemContract
   145  	ViewResolver     SystemContract
   146  
   147  	// EVM related contracts
   148  	EVMContract SystemContract
   149  	EVMStorage  SystemAccount
   150  }
   151  
   152  // AsTemplateEnv returns a template environment with all system contracts filled in.
   153  // This is useful for generating Cadence code from templates.
   154  func (c SystemContracts) AsTemplateEnv() templates.Environment {
   155  	return templates.Environment{
   156  		EpochAddress:             c.Epoch.Address.Hex(),
   157  		IDTableAddress:           c.IDTableStaking.Address.Hex(),
   158  		QuorumCertificateAddress: c.ClusterQC.Address.Hex(),
   159  		DkgAddress:               c.DKG.Address.Hex(),
   160  
   161  		ServiceAccountAddress:      c.FlowServiceAccount.Address.Hex(),
   162  		NodeVersionBeaconAddress:   c.NodeVersionBeacon.Address.Hex(),
   163  		RandomBeaconHistoryAddress: c.RandomBeaconHistory.Address.Hex(),
   164  		StorageFeesAddress:         c.FlowStorageFees.Address.Hex(),
   165  
   166  		FlowFeesAddress:      c.FlowFees.Address.Hex(),
   167  		FlowTokenAddress:     c.FlowToken.Address.Hex(),
   168  		FungibleTokenAddress: c.FungibleToken.Address.Hex(),
   169  
   170  		// The following contracts dont exist on the template env yet
   171  		// that is not a problem, but they are still listed here for completeness.
   172  
   173  		// NonFungibleToken: c.NonFungibleToken.Address.Hex(),
   174  		// MetadataViews : c.MetadataViews.Address.Hex(),
   175  		// ViewResolver : c.ViewResolver.Address.Hex(),
   176  
   177  		// EVMAddress: c.EVM.Address.Hex(),
   178  	}
   179  }
   180  
   181  // All returns all system contracts as a slice.
   182  func (c SystemContracts) All() []SystemContract {
   183  	return []SystemContract{
   184  		c.Epoch,
   185  		c.IDTableStaking,
   186  		c.ClusterQC,
   187  		c.DKG,
   188  
   189  		c.FlowServiceAccount,
   190  		c.NodeVersionBeacon,
   191  		c.RandomBeaconHistory,
   192  		c.FlowStorageFees,
   193  
   194  		c.FlowFees,
   195  		c.FlowToken,
   196  		c.FungibleToken,
   197  
   198  		c.NonFungibleToken,
   199  		c.MetadataViews,
   200  		c.ViewResolver,
   201  
   202  		c.EVMContract,
   203  		// EVMStorage is not included here, since it is not a contract
   204  	}
   205  }
   206  
   207  // ServiceEvents is a container for all service events on a particular chain.
   208  type ServiceEvents struct {
   209  	EpochSetup    ServiceEvent
   210  	EpochCommit   ServiceEvent
   211  	VersionBeacon ServiceEvent
   212  }
   213  
   214  // All returns all service events as a slice.
   215  func (se ServiceEvents) All() []ServiceEvent {
   216  	return []ServiceEvent{
   217  		se.EpochSetup,
   218  		se.EpochCommit,
   219  		se.VersionBeacon,
   220  	}
   221  }
   222  
   223  // SystemContractsForChain returns the system contract configuration for the given chain.
   224  // Panics if the chain is unknown.
   225  func SystemContractsForChain(chainID flow.ChainID) *SystemContracts {
   226  	contracts, ok := systemContractsForChain[chainID]
   227  	if !ok {
   228  		// this is a panic, since it can only happen if the code is wrong
   229  		panic(fmt.Sprintf("unknown chain: %s", chainID))
   230  	}
   231  	return contracts
   232  }
   233  
   234  var systemContractsForChain = map[flow.ChainID]*SystemContracts{}
   235  
   236  // ServiceEventsForChain returns the service event confirmation for the given chain.
   237  // Panics if the chain is unknown.
   238  func ServiceEventsForChain(chainID flow.ChainID) *ServiceEvents {
   239  	events, ok := serviceEventsForChain[chainID]
   240  	if !ok {
   241  		// this is a panic, since it can only happen if the code is wrong
   242  		panic(fmt.Sprintf("unknown chain: %s", chainID))
   243  	}
   244  	return events
   245  }
   246  
   247  var serviceEventsForChain = map[flow.ChainID]*ServiceEvents{}
   248  
   249  var contractAddressFunc = map[string]func(id flow.ChainID) flow.Address{}
   250  
   251  func init() {
   252  
   253  	serviceAddressFunc := func(chain flow.ChainID) flow.Address {
   254  		return chain.Chain().ServiceAddress()
   255  	}
   256  
   257  	// epoch contracts are deployed on a separate account on mainnet and testnet
   258  	epochAddressFunc := func(chain flow.ChainID) flow.Address {
   259  		switch chain {
   260  		case flow.Mainnet:
   261  			return stakingContractAddressMainnet
   262  		case flow.Testnet:
   263  			return stakingContractAddressTestnet
   264  		default:
   265  			return chain.Chain().ServiceAddress()
   266  		}
   267  	}
   268  
   269  	// some contracts are always at an address with a a predetermined index
   270  	nthAddressFunc := func(index uint64) func(chain flow.ChainID) flow.Address {
   271  		return func(chain flow.ChainID) flow.Address {
   272  			address, err := chain.Chain().AddressAtIndex(index)
   273  			if err != nil {
   274  				// this can only happen if the code is wrong
   275  				panic(fmt.Sprintf("failed to get %d address: %v", FlowFeesAccountIndex, err))
   276  			}
   277  			return address
   278  		}
   279  	}
   280  
   281  	nftTokenAddressFunc := func(chain flow.ChainID) flow.Address {
   282  		switch chain {
   283  		case flow.Mainnet:
   284  			return nftTokenAddressMainnet
   285  		case flow.Testnet:
   286  			return nftTokenAddressTestnet
   287  		default:
   288  			return chain.Chain().ServiceAddress()
   289  		}
   290  	}
   291  
   292  	evmStorageEVMFunc := func(chain flow.ChainID) flow.Address {
   293  		switch chain {
   294  		case flow.Mainnet:
   295  			return evmStorageAddressMainnet
   296  		case flow.Testnet:
   297  			return evmStorageAddressTestnet
   298  		default:
   299  			return nthAddressFunc(EVMStorageAccountIndex)(chain)
   300  		}
   301  	}
   302  
   303  	contractAddressFunc = map[string]func(id flow.ChainID) flow.Address{
   304  		ContractNameIDTableStaking: epochAddressFunc,
   305  		ContractNameEpoch:          epochAddressFunc,
   306  		ContractNameClusterQC:      epochAddressFunc,
   307  		ContractNameDKG:            epochAddressFunc,
   308  
   309  		ContractNameNodeVersionBeacon:   serviceAddressFunc,
   310  		ContractNameRandomBeaconHistory: serviceAddressFunc,
   311  		ContractNameServiceAccount:      serviceAddressFunc,
   312  		ContractNameStorageFees:         serviceAddressFunc,
   313  
   314  		ContractNameFlowFees:      nthAddressFunc(FlowFeesAccountIndex),
   315  		ContractNameFungibleToken: nthAddressFunc(FungibleTokenAccountIndex),
   316  		ContractNameFlowToken:     nthAddressFunc(FlowTokenAccountIndex),
   317  
   318  		ContractNameNonFungibleToken: nftTokenAddressFunc,
   319  		ContractNameMetadataViews:    nftTokenAddressFunc,
   320  		ContractNameViewResolver:     nftTokenAddressFunc,
   321  
   322  		ContractNameEVM:       serviceAddressFunc,
   323  		AccountNameEVMStorage: evmStorageEVMFunc,
   324  	}
   325  
   326  	getSystemContractsForChain := func(chainID flow.ChainID) *SystemContracts {
   327  
   328  		addressOfContract := func(name string) SystemContract {
   329  			addressFunc, ok := contractAddressFunc[name]
   330  			if !ok {
   331  				// this is a panic, since it can only happen if the code is wrong
   332  				panic(fmt.Sprintf("unknown system contract name: %s", name))
   333  			}
   334  
   335  			return SystemContract{
   336  				Address: addressFunc(chainID),
   337  				Name:    name,
   338  			}
   339  		}
   340  
   341  		addressOfAccount := func(name string) SystemAccount {
   342  			addressFunc, ok := contractAddressFunc[name]
   343  			if !ok {
   344  				// this is a panic, since it can only happen if the code is wrong
   345  				panic(fmt.Sprintf("unknown system account name: %s", name))
   346  			}
   347  
   348  			return SystemAccount{
   349  				Address: addressFunc(chainID),
   350  				Name:    name,
   351  			}
   352  		}
   353  
   354  		contracts := &SystemContracts{
   355  			Epoch:          addressOfContract(ContractNameEpoch),
   356  			IDTableStaking: addressOfContract(ContractNameIDTableStaking),
   357  			ClusterQC:      addressOfContract(ContractNameClusterQC),
   358  			DKG:            addressOfContract(ContractNameDKG),
   359  
   360  			FlowServiceAccount:  addressOfContract(ContractNameServiceAccount),
   361  			NodeVersionBeacon:   addressOfContract(ContractNameNodeVersionBeacon),
   362  			RandomBeaconHistory: addressOfContract(ContractNameRandomBeaconHistory),
   363  			FlowStorageFees:     addressOfContract(ContractNameStorageFees),
   364  
   365  			FlowFees:      addressOfContract(ContractNameFlowFees),
   366  			FlowToken:     addressOfContract(ContractNameFlowToken),
   367  			FungibleToken: addressOfContract(ContractNameFungibleToken),
   368  
   369  			NonFungibleToken: addressOfContract(ContractNameNonFungibleToken),
   370  			MetadataViews:    addressOfContract(ContractNameMetadataViews),
   371  			ViewResolver:     addressOfContract(ContractNameViewResolver),
   372  
   373  			EVMContract: addressOfContract(ContractNameEVM),
   374  			EVMStorage:  addressOfAccount(AccountNameEVMStorage),
   375  		}
   376  
   377  		return contracts
   378  	}
   379  
   380  	getServiceEventsForChain := func(chainID flow.ChainID) *ServiceEvents {
   381  
   382  		event := func(contractName, eventName string) ServiceEvent {
   383  			addressFunc, ok := contractAddressFunc[contractName]
   384  			if !ok {
   385  				// this is a panic, since it can only happen if the code is wrong
   386  				panic(fmt.Sprintf("unknown system contract name: %s", contractName))
   387  			}
   388  
   389  			return ServiceEvent{
   390  				Address:      addressFunc(chainID),
   391  				ContractName: contractName,
   392  				Name:         eventName,
   393  			}
   394  		}
   395  
   396  		events := &ServiceEvents{
   397  			EpochSetup:    event(ContractNameEpoch, EventNameEpochSetup),
   398  			EpochCommit:   event(ContractNameEpoch, EventNameEpochCommit),
   399  			VersionBeacon: event(ContractNameNodeVersionBeacon, EventNameVersionBeacon),
   400  		}
   401  
   402  		return events
   403  	}
   404  
   405  	// pre-populate the system contracts and service events for all chains for fast access
   406  	for _, chain := range flow.AllChainIDs() {
   407  		serviceEventsForChain[chain] = getServiceEventsForChain(chain)
   408  		systemContractsForChain[chain] = getSystemContractsForChain(chain)
   409  	}
   410  }