github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/util/natives/templates/solidity_templates.go (about)

     1  // Copyright Monax Industries Limited
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package templates
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"strings"
    10  	"text/template"
    11  
    12  	"github.com/hyperledger/burrow/execution/native"
    13  	"github.com/iancoleman/strcase"
    14  )
    15  
    16  const contractTemplateText = `pragma solidity [[.SolidityPragmaVersion]];
    17  
    18  /**
    19  [[.Comment]]
    20  * @dev These functions can be accessed as if this contract were deployed at a special address ([[.Address]]).
    21  * @dev This special address is defined as the last 20 bytes of the sha3 hash of the the contract name.
    22  * @dev To instantiate the contract use:
    23  * @dev [[.Name]] [[.InstanceName]] = [[.Name]](address(uint256(keccak256("[[.Name]]"))));
    24  */
    25  interface [[.Name]] {[[range .Functions]]
    26  [[.SolidityIndent 1]]
    27  [[end]]}
    28  `
    29  const functionTemplateText = `/**
    30  [[.Comment]]
    31  */
    32  function [[.Name]]([[.ArgList]]) external returns ([[.RetList]]);`
    33  
    34  // Solidity style guide recommends 4 spaces per indentation level
    35  // (see: http://solidity.readthedocs.io/en/develop/style-guide.html)
    36  const indentString = "    "
    37  
    38  var contractTemplate *template.Template
    39  var functionTemplate *template.Template
    40  
    41  func init() {
    42  	var err error
    43  	functionTemplate, err = template.New("SolidityFunctionTemplate").
    44  		Delims("[[", "]]").
    45  		Parse(functionTemplateText)
    46  	if err != nil {
    47  		panic(fmt.Errorf("couldn't parse native function template: %s", err))
    48  	}
    49  	contractTemplate, err = template.New("SolidityContractTemplate").
    50  		Delims("[[", "]]").
    51  		Parse(contractTemplateText)
    52  	if err != nil {
    53  		panic(fmt.Errorf("couldn't parse native contract template: %s", err))
    54  	}
    55  }
    56  
    57  type solidityContract struct {
    58  	SolidityPragmaVersion string
    59  	*native.Contract
    60  }
    61  
    62  type solidityFunction struct {
    63  	*native.Function
    64  }
    65  
    66  //
    67  // Contract
    68  //
    69  
    70  // Create a templated solidityContract from an native contract description
    71  func NewSolidityContract(contract *native.Contract) *solidityContract {
    72  	return &solidityContract{
    73  		SolidityPragmaVersion: ">=0.4.24",
    74  		Contract:              contract,
    75  	}
    76  }
    77  
    78  func (contract *solidityContract) Comment() string {
    79  	return comment(contract.Contract.Comment)
    80  }
    81  
    82  // Get a version of the contract name to be used for an instance of the contract
    83  func (contract *solidityContract) InstanceName() string {
    84  	// Hopefully the contract name is UpperCamelCase. If it's not, oh well, this
    85  	// is meant to be illustrative rather than cast iron compilable
    86  	instanceName := strings.ToLower(contract.Name[:1]) + contract.Name[1:]
    87  	if instanceName == contract.Name {
    88  		return "contractInstance"
    89  	}
    90  	return instanceName
    91  }
    92  
    93  func (contract *solidityContract) Address() string {
    94  	return fmt.Sprintf("0x%s",
    95  		contract.Contract.Address())
    96  }
    97  
    98  // Generate Solidity code for this native contract
    99  func (contract *solidityContract) Solidity() (string, error) {
   100  	buf := new(bytes.Buffer)
   101  	err := contractTemplate.Execute(buf, contract)
   102  	if err != nil {
   103  		return "", err
   104  	}
   105  	return buf.String(), nil
   106  }
   107  
   108  func (contract *solidityContract) Functions() []*solidityFunction {
   109  	functions := contract.Contract.Functions()
   110  	solidityFunctions := make([]*solidityFunction, len(functions))
   111  	for i, function := range functions {
   112  		solidityFunctions[i] = NewSolidityFunction(function)
   113  	}
   114  	return solidityFunctions
   115  }
   116  
   117  //
   118  // Function
   119  //
   120  
   121  // Create a templated solidityFunction from an native function description
   122  func NewSolidityFunction(function *native.Function) *solidityFunction {
   123  	return &solidityFunction{function}
   124  }
   125  
   126  func (function *solidityFunction) ArgList() string {
   127  	abi := function.Abi()
   128  	argList := make([]string, len(abi.Inputs))
   129  	for i, arg := range abi.Inputs {
   130  		storage := ""
   131  		if arg.EVM.Dynamic() {
   132  			storage = " calldata"
   133  		}
   134  		argList[i] = fmt.Sprintf("%s%s %s", arg.EVM.GetSignature(), storage, param(arg.Name))
   135  	}
   136  	return strings.Join(argList, ", ")
   137  }
   138  
   139  func (function *solidityFunction) RetList() string {
   140  	abi := function.Abi()
   141  	argList := make([]string, len(abi.Outputs))
   142  	for i, arg := range abi.Outputs {
   143  		argList[i] = fmt.Sprintf("%s %s", arg.EVM.GetSignature(), param(arg.Name))
   144  	}
   145  	return strings.Join(argList, ", ")
   146  }
   147  
   148  func (function *solidityFunction) Comment() string {
   149  	return comment(function.Function.Comment)
   150  }
   151  
   152  func (function *solidityFunction) SolidityIndent(indentLevel uint) (string, error) {
   153  	return function.solidity(indentLevel)
   154  }
   155  
   156  func (function *solidityFunction) Solidity() (string, error) {
   157  	return function.solidity(0)
   158  }
   159  
   160  func (function *solidityFunction) solidity(indentLevel uint) (string, error) {
   161  	buf := new(bytes.Buffer)
   162  	iw := NewIndentWriter(indentLevel, indentString, buf)
   163  	err := functionTemplate.Execute(iw, function)
   164  	if err != nil {
   165  		return "", err
   166  	}
   167  	return buf.String(), nil
   168  }
   169  
   170  //
   171  // Utility
   172  //
   173  
   174  func comment(comment string) string {
   175  	commentLines := make([]string, 0, 5)
   176  	for _, line := range strings.Split(comment, "\n") {
   177  		trimLine := strings.TrimLeft(line, " \t\n")
   178  		if trimLine != "" {
   179  			commentLines = append(commentLines, trimLine)
   180  		}
   181  	}
   182  	return strings.Join(commentLines, "\n")
   183  }
   184  
   185  func param(name string) string {
   186  	return "_" + strcase.ToSnake(name)
   187  }