diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bc0ec20..d236d6a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -12,12 +12,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 - - uses: actions/setup-go@v4 + uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: - go-version: "1.21.*" + go-version: "1.22.*" - name: golangci-lint - uses: golangci/golangci-lint-action@v2 + uses: golangci/golangci-lint-action@v6 with: args: --timeout=3m @@ -25,22 +25,22 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 - - uses: actions/setup-go@v4 + uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: - go-version: "1.21.*" + go-version: "1.22.*" - uses: zencargo/github-action-go-mod-tidy@v1 with: - go-version: "1.21" + go-version: "1.22" test: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 - - uses: actions/setup-go@v4 + uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: - go-version: "1.21" + go-version: "1.22" - run: make test-report - name: Publish Unit Test Results uses: EnricoMi/publish-unit-test-result-action@v1 @@ -52,10 +52,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 - - uses: actions/setup-go@v4 + uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: - go-version: "1.21" + go-version: "1.22" - run: make coveralls - uses: shogo82148/actions-goveralls@v1 if: always() diff --git a/contracts/Debug.cdc b/contracts/Debug.cdc index 42c324b..4e9657b 100644 --- a/contracts/Debug.cdc +++ b/contracts/Debug.cdc @@ -1,4 +1,4 @@ -import NonFungibleToken from "NonFungibleToken.cdc" +import "NonFungibleToken" access(all) contract Debug { diff --git a/event.go b/event.go index af0f995..16df801 100644 --- a/event.go +++ b/event.go @@ -77,7 +77,7 @@ func (me OverflowEvent) GetStakeholders() map[string][]string { strings.Contains(eventName, "FungibleToken.Deposited") || strings.Contains(eventName, "NonFungibleToken.Deposited") || strings.Contains(eventName, "NonFungibleToken.Withdrawn") { - vaultType := me.Fields["type"].(string) + vaultType, _ := me.Fields["type"].(string) existing = append(existing, fmt.Sprintf("%s/%s", vaultType, name)) } else { existing = append(existing, fmt.Sprintf("%s/%s", me.Name, name)) @@ -241,11 +241,11 @@ func (overflowEvents OverflowEvents) FilterFees(fee float64, payer string) Overf if strings.HasSuffix(name, ".FungibleToken.Withdrawn") { withDrawnEvents := []OverflowEvent{} for _, value := range events { - ftType := value.Fields["type"].(string) + ftType, _ := value.Fields["type"].(string) if !strings.HasSuffix(ftType, "FlowToken.Vault") { continue } - amount := value.Fields["amount"].(float64) + amount, _ := value.Fields["amount"].(float64) from, ok := value.Fields["from"].(string) if ok && amount == fee && from == payer { @@ -264,11 +264,11 @@ func (overflowEvents OverflowEvents) FilterFees(fee float64, payer string) Overf if strings.HasSuffix(name, ".FungibleToken.Deposited") { depositEvents := []OverflowEvent{} for _, value := range events { - ftType := value.Fields["type"].(string) + ftType, _ := value.Fields["type"].(string) if !strings.HasSuffix(ftType, "FlowToken.Vault") { continue } - amount := value.Fields["amount"].(float64) + amount, _ := value.Fields["amount"].(float64) to, ok := value.Fields["to"].(string) if ok && amount == fee && slices.Contains(feeReceipients, to) { @@ -288,7 +288,7 @@ func (overflowEvents OverflowEvents) FilterFees(fee float64, payer string) Overf withDrawnEvents := []OverflowEvent{} for _, value := range events { - amount := value.Fields["amount"].(float64) + amount, _ := value.Fields["amount"].(float64) from, ok := value.Fields["from"].(string) if ok && amount == fee && from == payer { @@ -308,7 +308,7 @@ func (overflowEvents OverflowEvents) FilterFees(fee float64, payer string) Overf depositEvents := []OverflowEvent{} for _, value := range events { - amount := value.Fields["amount"].(float64) + amount, _ := value.Fields["amount"].(float64) to, ok := value.Fields["to"].(string) if ok && amount == fee && slices.Contains(feeReceipients, to) { diff --git a/go.mod b/go.mod index cab749b..92e8ea2 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,10 @@ go 1.21 require ( github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de - github.com/bjartek/underflow v1.3.0 + github.com/bjartek/underflow v1.5.0 github.com/enescakir/emoji v1.0.0 github.com/fatih/color v1.16.0 + github.com/hashicorp/go-multierror v1.1.1 github.com/hexops/autogold v1.3.1 github.com/onflow/cadence v1.0.0-preview.25 github.com/onflow/flixkit-go v1.2.1-cadence-v1-preview.10 @@ -92,7 +93,6 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect diff --git a/go.sum b/go.sum index c3274a1..8b45ba6 100644 --- a/go.sum +++ b/go.sum @@ -1074,8 +1074,8 @@ github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edY github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/bjartek/underflow v1.3.0 h1:HOE1ZEuAMkFUXCsXwV+gCFUHjyJA9d9nTIJu+RHI7SI= -github.com/bjartek/underflow v1.3.0/go.mod h1:JO6QNXSxgqr1CeegDk3DXbNhffRP1K/+DPBvB8Q+/N8= +github.com/bjartek/underflow v1.5.0 h1:0EJK6DmueoO+ffKzH8uhXkuVPL6FHiAQdzBsfrQ9s5Q= +github.com/bjartek/underflow v1.5.0/go.mod h1:M+rSteYN7KtCDUIjILxxUJRFyhpgOM1WFujJzyTDIP4= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= diff --git a/interaction_builder.go b/interaction_builder.go index 417bae9..d4bcd8c 100644 --- a/interaction_builder.go +++ b/interaction_builder.go @@ -75,7 +75,7 @@ type OverflowInteractionBuilder struct { // Event filters to apply to the interaction EventFilter OverflowEventFilter - // Wheter to ignore global event filters from OverflowState or not + // Whether to ignore global event filters from OverflowState or not IgnoreGlobalEventFilters bool // Options to use when printing results diff --git a/parse_integration_test.go b/parse_integration_test.go index 05a5835..f87e693 100644 --- a/parse_integration_test.go +++ b/parse_integration_test.go @@ -11,8 +11,8 @@ import ( ) func TestParseConfig(t *testing.T) { - g, err := OverflowTesting() - require.NoError(t, err) + g, err2 := OverflowTesting() + require.NoError(t, err2) require.NotNil(t, g) t.Run("parse", func(t *testing.T) { diff --git a/result.go b/result.go index 5c3daa3..3703555 100644 --- a/result.go +++ b/result.go @@ -384,7 +384,7 @@ func getByteArray(data interface{}) ([]byte, error) { if !ok { return nil, fmt.Errorf("unexpected type at index %d", i) } - byteSlice[i] = byte(b) + byteSlice[i] = b } return byteSlice, nil } diff --git a/setup.go b/setup.go index 1f75dc2..ca0538f 100644 --- a/setup.go +++ b/setup.go @@ -175,8 +175,21 @@ func (o *OverflowBuilder) StartResult() *OverflowState { if o.InputResolver != nil { overflow.InputResolver = *o.InputResolver } else { - overflow.InputResolver = func(name string) (string, error) { - return overflow.QualifiedIdentifierFromSnakeCase(name) + overflow.InputResolver = func(name string, resolveType underflow.ResolveType) (string, error) { + if resolveType == underflow.Identifier { + return overflow.QualifiedIdentifierFromSnakeCase(name) + } + + adr, err2 := hexToAddress(name) + if err2 == nil { + return adr.String(), nil + } + + address, err2 := overflow.FlowAddressE(name) + if err2 != nil { + return "", errors.Wrapf(err2, "could not parse %s into an address", name) + } + return address.HexWithPrefix(), nil } } network, err := state.Networks().ByName(o.Network) @@ -240,7 +253,6 @@ func (o *OverflowBuilder) StartResult() *OverflowState { return overflow } } - if o.DeployContracts { overflow = overflow.InitializeContracts(o.Ctx) if overflow.Error != nil { diff --git a/state.go b/state.go index 85fff25..c04967b 100644 --- a/state.go +++ b/state.go @@ -14,12 +14,12 @@ import ( "github.com/bjartek/underflow" "github.com/enescakir/emoji" + "github.com/hashicorp/go-multierror" "github.com/onflow/cadence" "github.com/onflow/cadence/runtime" "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/cmd" "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/sema" "github.com/onflow/flixkit-go/flixkit" "github.com/onflow/flow-go-sdk" @@ -156,7 +156,7 @@ type OverflowState struct { type OverflowArgument struct { Value interface{} - Type ast.Type + Type sema.Type Name string } @@ -223,17 +223,12 @@ func (o *OverflowState) QualifiedIdentifier(contract string, name string) (strin return "", fmt.Errorf("you are trying to get the qualified identifier for something you are not creating or have mentioned in flow.json with name=%s", contract) } -func (o *OverflowState) parseArguments(fileName string, code []byte, inputArgs map[string]interface{}) ([]cadence.Value, CadenceArguments, error) { - resultArgs := make([]cadence.Value, 0) - resultArgsMap := CadenceArguments{} - +func ExtractArguments(fileName string, code []byte, inputArgs map[string]interface{}) (OverflowArgumentList, error) { codes := map[common.Location][]byte{} location := common.StringLocation(fileName) program, must := cmd.PrepareProgram(code, location, codes) checker, _ := cmd.PrepareChecker(program, location, codes, nil, nil, must) - var parameterList []*ast.Parameter - functionDeclaration := sema.FunctionEntryPointDeclaration(program) if functionDeclaration != nil { if functionDeclaration.ParameterList != nil { @@ -248,9 +243,6 @@ func (o *OverflowState) parseArguments(fileName string, code []byte, inputArgs m } } - if parameterList == nil { - return resultArgs, resultArgsMap, nil - } argumentNotPresent := []string{} argumentNames := []string{} args := OverflowArgumentList{} @@ -264,19 +256,18 @@ func (o *OverflowState) parseArguments(fileName string, code []byte, inputArgs m args = append(args, OverflowArgument{ Name: parameterName, Value: value, - Type: parameter.TypeAnnotation.Type, + Type: checker.ConvertType(parameter.TypeAnnotation.Type), }) } } if len(argumentNotPresent) > 0 { err := fmt.Errorf("the interaction '%s' is missing %v", fileName, argumentNotPresent) - return nil, nil, err + return nil, err } redundantArgument := []string{} for inputKey := range inputArgs { - // If your IDE complains about this it is wrong, this is 1.18 generics not suported anywhere if !slices.Contains(argumentNames, inputKey) { redundantArgument = append(redundantArgument, inputKey) } @@ -284,74 +275,33 @@ func (o *OverflowState) parseArguments(fileName string, code []byte, inputArgs m if len(redundantArgument) > 0 { err := fmt.Errorf("the interaction '%s' has the following extra arguments %v", fileName, redundantArgument) - return nil, nil, err + return nil, err } - for _, oa := range args { - - name := oa.Name - argument := oa.Value - - cadenceVal, isCadenceValue := argument.(cadence.Value) - if isCadenceValue { - resultArgs = append(resultArgs, cadenceVal) - resultArgsMap[name] = cadenceVal - continue - } - - var argumentString string - switch a := argument.(type) { - case nil: - argumentString = "nil" - case string: - argumentString = a - case int: - argumentString = fmt.Sprintf("%v", a) - default: - cadenceVal, err := underflow.InputToCadence(argument, o.InputResolver) - if err != nil { - return nil, nil, err - } - resultArgs = append(resultArgs, cadenceVal) - resultArgsMap[name] = cadenceVal - continue - - } - semaType := checker.ConvertType(oa.Type) - - switch semaType { - case sema.StringType: - if len(argumentString) > 0 && !strings.HasPrefix(argumentString, "\"") { - argumentString = "\"" + argumentString + "\"" - } - } - - switch semaType.(type) { - case *sema.AddressType: - - account, _ := o.AccountE(argumentString) - - if account != nil { - argumentString = account.Address.String() - } + if parameterList == nil { + return nil, nil + } + return args, nil +} - if !strings.Contains(argumentString, "0x") { - argumentString = fmt.Sprintf("0x%s", argumentString) - } - } +func (o *OverflowState) parseArguments(fileName string, code []byte, inputArgs map[string]interface{}) ([]cadence.Value, CadenceArguments, error) { + resultArgs := make([]cadence.Value, 0) + resultArgsMap := CadenceArguments{} - inter, interErr := interpreter.NewInterpreter(nil, nil, &interpreter.Config{}) - if interErr != nil { - return nil, nil, interErr - } - value, err := runtime.ParseLiteral(argumentString, semaType, inter) + args, err := ExtractArguments(fileName, code, inputArgs) + if err != nil { + return nil, nil, errors.Wrap(err, "extracting arguments") + } + var multiErr *multierror.Error + for _, oa := range args { + cadenceVal, err := underflow.InputToCadenceWithHint(oa.Value, oa.Type, o.InputResolver) if err != nil { - return nil, nil, errors.Wrapf(err, "argument `%s` with value `%s` is not expected type `%s`", name, argumentString, semaType) + multiErr = multierror.Append(errors.Wrapf(err, "argument `%s` with value `%s` is not expected type `%s`", oa.Name, oa.Value, oa.Type)) } - resultArgs = append(resultArgs, value) - resultArgsMap[name] = value + resultArgs = append(resultArgs, cadenceVal) + resultArgsMap[oa.Name] = cadenceVal } - return resultArgs, resultArgsMap, nil + return resultArgs, resultArgsMap, multiErr.ErrorOrNil() } func (o *OverflowState) AccountPublicKey(name string) (string, error) { @@ -389,36 +339,46 @@ func (o *OverflowState) Address(key string) string { } // return the flow Address of the given name -func (o *OverflowState) FlowAddress(key string) flow.Address { +func (o *OverflowState) FlowAddressE(key string) (*flow.Address, error) { account, err := o.AccountE(key) if err == nil { - return account.Address + return &account.Address, nil } flowContract, err := o.State.Contracts().ByName(key) if err != nil { - panic(err) + return nil, err } // we found the contract specified in contracts section if flowContract != nil { alias := flowContract.Aliases.ByNetwork(o.Network.Name) if alias != nil { - return alias.Address + return &alias.Address, nil } } flowDeploymentContracts, err := o.State.DeploymentContractsByNetwork(o.Network) if err != nil { - panic(err) + return nil, err } for _, flowDeploymentContract := range flowDeploymentContracts { if flowDeploymentContract.Name == key { - return flowDeploymentContract.AccountAddress + return &flowDeploymentContract.AccountAddress, nil } } - panic("Not valid user account, contract or deployment contract") + return nil, fmt.Errorf("account with name=%s is not valid user account, contract or deployment contract", key) +} + +// return the flow Address of the given name +// DEPRECATED: use FlowAddressE +func (o *OverflowState) FlowAddress(key string) flow.Address { + address, err := o.FlowAddressE(key) + if err != nil { + panic(err.Error) + } + return *address } // return the account of a given account @@ -487,7 +447,7 @@ func (o *OverflowState) CreateAccountsE(ctx context.Context) (*OverflowState, er if o.Network.Name == "emulator" && o.NewUserFlowAmount != 0.0 { res := o.MintFlowTokens(account.Address.String(), o.NewUserFlowAmount) if res.Error != nil { - return nil, errors.Wrap(err, "could not mint flow tokens") + return nil, errors.Wrap(res.Error, "could not mint flow tokens") } messages = append(messages, "with flow:", fmt.Sprintf("%.2f", o.NewUserFlowAmount)) } @@ -561,7 +521,7 @@ func (o OverflowState) readLog() ([]OverflowEmulatorLogMessage, error) { delete(msg, "level") rawCom, ok := msg["computationUsed"] if ok { - field := rawCom.(float64) + field, _ := rawCom.(float64) doc.ComputationUsed = int(field) delete(msg, "computationUsed") } @@ -748,9 +708,9 @@ func (o *OverflowState) ParseAllWithConfig(skipContracts bool, txSkip []string, if strings.HasSuffix(path, ".cdc") { name := strings.TrimSuffix(info.Name(), ".cdc") for _, txSkip := range txSkip { - match, err := regexp.MatchString(txSkip, name) - if err != nil { - return err + match, err2 := regexp.MatchString(txSkip, name) + if err2 != nil { + return err2 } if match { return nil @@ -769,9 +729,9 @@ func (o *OverflowState) ParseAllWithConfig(skipContracts bool, txSkip []string, if strings.HasSuffix(path, ".cdc") { name := strings.TrimSuffix(info.Name(), ".cdc") for _, scriptSkip := range txSkip { - match, err := regexp.MatchString(scriptSkip, name) + match, err2 := regexp.MatchString(scriptSkip, name) if err != nil { - return err + return err2 } if match { return nil diff --git a/transaction_integration_test.go b/transaction_integration_test.go index 28c08bd..f500724 100644 --- a/transaction_integration_test.go +++ b/transaction_integration_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + "github.com/bjartek/underflow" "github.com/onflow/flow-go/utils/io" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -14,11 +15,11 @@ import ( */ func TestTransactionIntegration(t *testing.T) { - customResolver := func(input string) (string, error) { + customResolver := func(input string, _ underflow.ResolveType) (string, error) { return "A.f8d6e0586b0a20c7.Debug.Foo", nil } - o, err := OverflowTesting(WithCoverageReport()) - require.NoError(t, err) + o, errG := OverflowTesting(WithCoverageReport()) + require.NoError(t, errG) require.NotNil(t, o) o.Tx("mint_tokens", WithSignerServiceAccount(), WithArg("recipient", "first"), WithArg("amount", 1.0)).AssertSuccess(t) @@ -163,13 +164,13 @@ func TestTransactionIntegration(t *testing.T) { AssertSuccess(t) var events []interface{} - err := result.MarshalEventsWithName("TokensDeposited", &events) - assert.NoError(t, err) + err2 := result.MarshalEventsWithName("TokensDeposited", &events) + assert.NoError(t, err2) assert.Equal(t, 1, len(events)) var singleEvent interface{} - err = result.GetEventsWithName("TokensDeposited")[0].MarshalAs(&singleEvent) - assert.NoError(t, err) + err2 = result.GetEventsWithName("TokensDeposited")[0].MarshalAs(&singleEvent) + assert.NoError(t, err2) assert.NotNil(t, singleEvent) }) diff --git a/transaction_test.go b/transaction_test.go index dd45dbe..6ddd519 100644 --- a/transaction_test.go +++ b/transaction_test.go @@ -191,7 +191,7 @@ transaction(test:Address) { } } `, "transaction", WithArg("test", "bjartek"), WithSignerServiceAccount()) - assert.ErrorContains(t, res.Error, "argument `test` with value `0xbjartek` is not expected type `Address`") + assert.ErrorContains(t, res.Error, "argument `test` with value `bjartek` is not expected type `Address`") }) t.Run("Should set gas", func(t *testing.T) {