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 }