diff --git a/docs/api/README.md b/docs/api/README.md
index eb3ee14a7..51a790cdc 100644
--- a/docs/api/README.md
+++ b/docs/api/README.md
@@ -2636,6 +2636,7 @@ Authorization ( Scopes: ledger:write )
|script|object|false|none|none|
|» plain|string|true|none|none|
|» vars|object|false|none|none|
+|»» **additionalProperties**|string|false|none|none|
|reference|string|false|none|none|
|metadata|[V2Metadata](#schemav2metadata)|true|none|none|
@@ -3191,6 +3192,7 @@ Authorization ( Scopes: ledger:write )
|*anonymous*|BULK_SIZE_EXCEEDED|
|*anonymous*|INTERPRETER_PARSE|
|*anonymous*|INTERPRETER_RUNTIME|
+|*anonymous*|LEDGER_ALREADY_EXISTS|
V2LedgerInfoResponse
diff --git a/internal/api/v2/controllers_ledgers_create.go b/internal/api/v2/controllers_ledgers_create.go
index ace33cbc0..488b6d1d9 100644
--- a/internal/api/v2/controllers_ledgers_create.go
+++ b/internal/api/v2/controllers_ledgers_create.go
@@ -34,10 +34,11 @@ func createLedger(systemController system.Controller) http.HandlerFunc {
if err := systemController.CreateLedger(r.Context(), chi.URLParam(r, "ledger"), configuration); err != nil {
switch {
case errors.Is(err, system.ErrInvalidLedgerConfiguration{}) ||
- errors.Is(err, system.ErrLedgerAlreadyExists) ||
errors.Is(err, ledger.ErrInvalidLedgerName{}) ||
errors.Is(err, ledger.ErrInvalidBucketName{}):
api.BadRequest(w, ErrValidation, err)
+ case errors.Is(err, system.ErrLedgerAlreadyExists):
+ api.BadRequest(w, ErrLedgerAlreadyExists, err)
default:
common.HandleCommonErrors(w, r, err)
}
diff --git a/internal/api/v2/controllers_ledgers_create_test.go b/internal/api/v2/controllers_ledgers_create_test.go
index 7c302f0bf..4b0874292 100644
--- a/internal/api/v2/controllers_ledgers_create_test.go
+++ b/internal/api/v2/controllers_ledgers_create_test.go
@@ -55,7 +55,7 @@ func TestLedgersCreate(t *testing.T) {
expectedBackendCall: true,
returnErr: system.ErrLedgerAlreadyExists,
expectStatusCode: http.StatusBadRequest,
- expectErrorCode: ErrValidation,
+ expectErrorCode: ErrLedgerAlreadyExists,
},
{
name: "invalid ledger name",
diff --git a/internal/api/v2/errors.go b/internal/api/v2/errors.go
index 7306c6019..438a1e9ad 100644
--- a/internal/api/v2/errors.go
+++ b/internal/api/v2/errors.go
@@ -9,6 +9,7 @@ const (
ErrCompilationFailed = "COMPILATION_FAILED"
ErrMetadataOverride = "METADATA_OVERRIDE"
ErrBulkSizeExceeded = "BULK_SIZE_EXCEEDED"
+ ErrLedgerAlreadyExists = "LEDGER_ALREADY_EXISTS"
ErrInterpreterParse = "INTERPRETER_PARSE"
ErrInterpreterRuntime = "INTERPRETER_RUNTIME"
diff --git a/openapi.yaml b/openapi.yaml
index 2766718c9..13c7ef9ea 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -3296,7 +3296,8 @@ components:
vars:
type: object
properties: {}
- additionalProperties: true
+ additionalProperties:
+ type: string
example:
user: users:042
required:
@@ -3455,6 +3456,7 @@ components:
- BULK_SIZE_EXCEEDED
- INTERPRETER_PARSE
- INTERPRETER_RUNTIME
+ - LEDGER_ALREADY_EXISTS
example: VALIDATION
V2LedgerInfoResponse:
type: object
diff --git a/openapi/v2.yaml b/openapi/v2.yaml
index cb4b4f02f..77f31b9d8 100644
--- a/openapi/v2.yaml
+++ b/openapi/v2.yaml
@@ -1565,7 +1565,8 @@ components:
vars:
type: object
properties: {}
- additionalProperties: true
+ additionalProperties:
+ type: string
example:
user: users:042
required:
@@ -1725,6 +1726,7 @@ components:
- BULK_SIZE_EXCEEDED
- INTERPRETER_PARSE
- INTERPRETER_RUNTIME
+ - LEDGER_ALREADY_EXISTS
example: VALIDATION
V2LedgerInfoResponse:
type: object
diff --git a/pkg/client/.speakeasy/gen.lock b/pkg/client/.speakeasy/gen.lock
index 3bb1f546f..6ee5c6fe8 100644
--- a/pkg/client/.speakeasy/gen.lock
+++ b/pkg/client/.speakeasy/gen.lock
@@ -1,12 +1,12 @@
lockVersion: 2.0.0
id: a9ac79e1-e429-4ee3-96c4-ec973f19bec3
management:
- docChecksum: db305ef0d86f319f00b70be811eefeea
+ docChecksum: 169efa4fe3c5d6561f06920598d20df4
docVersion: v1
speakeasyVersion: 1.351.0
generationVersion: 2.384.1
- releaseVersion: 0.4.18
- configChecksum: 37cc61b909fcb3fb116f1cd48c1fc99a
+ releaseVersion: 0.4.20
+ configChecksum: e66e70c75590218ba585d230919d03e3
features:
go:
additionalDependencies: 0.1.0
diff --git a/pkg/client/.speakeasy/gen.yaml b/pkg/client/.speakeasy/gen.yaml
index c007341f1..2718b0b1c 100644
--- a/pkg/client/.speakeasy/gen.yaml
+++ b/pkg/client/.speakeasy/gen.yaml
@@ -15,7 +15,7 @@ generation:
auth:
oAuth2ClientCredentialsEnabled: true
go:
- version: 0.4.18
+ version: 0.4.20
additionalDependencies: {}
allowUnknownFieldsInWeakUnions: false
clientServerStatusCodesAsErrors: true
diff --git a/pkg/client/docs/models/components/v2errorsenum.md b/pkg/client/docs/models/components/v2errorsenum.md
index 35dc2f95a..f1757ffab 100644
--- a/pkg/client/docs/models/components/v2errorsenum.md
+++ b/pkg/client/docs/models/components/v2errorsenum.md
@@ -3,21 +3,22 @@
## Values
-| Name | Value |
-| -------------------------------- | -------------------------------- |
-| `V2ErrorsEnumInternal` | INTERNAL |
-| `V2ErrorsEnumInsufficientFund` | INSUFFICIENT_FUND |
-| `V2ErrorsEnumValidation` | VALIDATION |
-| `V2ErrorsEnumConflict` | CONFLICT |
-| `V2ErrorsEnumCompilationFailed` | COMPILATION_FAILED |
-| `V2ErrorsEnumMetadataOverride` | METADATA_OVERRIDE |
-| `V2ErrorsEnumNotFound` | NOT_FOUND |
-| `V2ErrorsEnumRevertOccurring` | REVERT_OCCURRING |
-| `V2ErrorsEnumAlreadyRevert` | ALREADY_REVERT |
-| `V2ErrorsEnumNoPostings` | NO_POSTINGS |
-| `V2ErrorsEnumLedgerNotFound` | LEDGER_NOT_FOUND |
-| `V2ErrorsEnumImport` | IMPORT |
-| `V2ErrorsEnumTimeout` | TIMEOUT |
-| `V2ErrorsEnumBulkSizeExceeded` | BULK_SIZE_EXCEEDED |
-| `V2ErrorsEnumInterpreterParse` | INTERPRETER_PARSE |
-| `V2ErrorsEnumInterpreterRuntime` | INTERPRETER_RUNTIME |
\ No newline at end of file
+| Name | Value |
+| --------------------------------- | --------------------------------- |
+| `V2ErrorsEnumInternal` | INTERNAL |
+| `V2ErrorsEnumInsufficientFund` | INSUFFICIENT_FUND |
+| `V2ErrorsEnumValidation` | VALIDATION |
+| `V2ErrorsEnumConflict` | CONFLICT |
+| `V2ErrorsEnumCompilationFailed` | COMPILATION_FAILED |
+| `V2ErrorsEnumMetadataOverride` | METADATA_OVERRIDE |
+| `V2ErrorsEnumNotFound` | NOT_FOUND |
+| `V2ErrorsEnumRevertOccurring` | REVERT_OCCURRING |
+| `V2ErrorsEnumAlreadyRevert` | ALREADY_REVERT |
+| `V2ErrorsEnumNoPostings` | NO_POSTINGS |
+| `V2ErrorsEnumLedgerNotFound` | LEDGER_NOT_FOUND |
+| `V2ErrorsEnumImport` | IMPORT |
+| `V2ErrorsEnumTimeout` | TIMEOUT |
+| `V2ErrorsEnumBulkSizeExceeded` | BULK_SIZE_EXCEEDED |
+| `V2ErrorsEnumInterpreterParse` | INTERPRETER_PARSE |
+| `V2ErrorsEnumInterpreterRuntime` | INTERPRETER_RUNTIME |
+| `V2ErrorsEnumLedgerAlreadyExists` | LEDGER_ALREADY_EXISTS |
\ No newline at end of file
diff --git a/pkg/client/docs/models/components/v2posttransactionscript.md b/pkg/client/docs/models/components/v2posttransactionscript.md
index 657c7dd0e..b92c259b4 100644
--- a/pkg/client/docs/models/components/v2posttransactionscript.md
+++ b/pkg/client/docs/models/components/v2posttransactionscript.md
@@ -6,4 +6,4 @@
| Field | Type | Required | Description | Example |
| -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `Plain` | *string* | :heavy_check_mark: | N/A | vars {
account $user
}
send [COIN 10] (
source = @world
destination = $user
)
|
-| `Vars` | map[string]*any* | :heavy_minus_sign: | N/A | {
"user": "users:042"
} |
\ No newline at end of file
+| `Vars` | map[string]*string* | :heavy_minus_sign: | N/A | {
"user": "users:042"
} |
\ No newline at end of file
diff --git a/pkg/client/docs/sdks/v2/README.md b/pkg/client/docs/sdks/v2/README.md
index 03958d3b0..9292a94ed 100644
--- a/pkg/client/docs/sdks/v2/README.md
+++ b/pkg/client/docs/sdks/v2/README.md
@@ -418,7 +418,7 @@ func main() {
destination = $user
)
",
- Vars: map[string]any{
+ Vars: map[string]string{
"user": "users:042",
},
},
@@ -959,7 +959,7 @@ func main() {
destination = $user
)
",
- Vars: map[string]any{
+ Vars: map[string]string{
"user": "users:042",
},
},
diff --git a/pkg/client/formance.go b/pkg/client/formance.go
index 66b87bf4e..2d2842cb3 100644
--- a/pkg/client/formance.go
+++ b/pkg/client/formance.go
@@ -143,9 +143,9 @@ func New(opts ...SDKOption) *Formance {
sdkConfiguration: sdkConfiguration{
Language: "go",
OpenAPIDocVersion: "v1",
- SDKVersion: "0.4.18",
+ SDKVersion: "0.4.20",
GenVersion: "2.384.1",
- UserAgent: "speakeasy-sdk/go 0.4.18 2.384.1 v1 github.com/formancehq/ledger/pkg/client",
+ UserAgent: "speakeasy-sdk/go 0.4.20 2.384.1 v1 github.com/formancehq/ledger/pkg/client",
Hooks: hooks.New(),
},
}
diff --git a/pkg/client/models/components/v2errorsenum.go b/pkg/client/models/components/v2errorsenum.go
index 862ca3c52..8bbc25e7a 100644
--- a/pkg/client/models/components/v2errorsenum.go
+++ b/pkg/client/models/components/v2errorsenum.go
@@ -10,22 +10,23 @@ import (
type V2ErrorsEnum string
const (
- V2ErrorsEnumInternal V2ErrorsEnum = "INTERNAL"
- V2ErrorsEnumInsufficientFund V2ErrorsEnum = "INSUFFICIENT_FUND"
- V2ErrorsEnumValidation V2ErrorsEnum = "VALIDATION"
- V2ErrorsEnumConflict V2ErrorsEnum = "CONFLICT"
- V2ErrorsEnumCompilationFailed V2ErrorsEnum = "COMPILATION_FAILED"
- V2ErrorsEnumMetadataOverride V2ErrorsEnum = "METADATA_OVERRIDE"
- V2ErrorsEnumNotFound V2ErrorsEnum = "NOT_FOUND"
- V2ErrorsEnumRevertOccurring V2ErrorsEnum = "REVERT_OCCURRING"
- V2ErrorsEnumAlreadyRevert V2ErrorsEnum = "ALREADY_REVERT"
- V2ErrorsEnumNoPostings V2ErrorsEnum = "NO_POSTINGS"
- V2ErrorsEnumLedgerNotFound V2ErrorsEnum = "LEDGER_NOT_FOUND"
- V2ErrorsEnumImport V2ErrorsEnum = "IMPORT"
- V2ErrorsEnumTimeout V2ErrorsEnum = "TIMEOUT"
- V2ErrorsEnumBulkSizeExceeded V2ErrorsEnum = "BULK_SIZE_EXCEEDED"
- V2ErrorsEnumInterpreterParse V2ErrorsEnum = "INTERPRETER_PARSE"
- V2ErrorsEnumInterpreterRuntime V2ErrorsEnum = "INTERPRETER_RUNTIME"
+ V2ErrorsEnumInternal V2ErrorsEnum = "INTERNAL"
+ V2ErrorsEnumInsufficientFund V2ErrorsEnum = "INSUFFICIENT_FUND"
+ V2ErrorsEnumValidation V2ErrorsEnum = "VALIDATION"
+ V2ErrorsEnumConflict V2ErrorsEnum = "CONFLICT"
+ V2ErrorsEnumCompilationFailed V2ErrorsEnum = "COMPILATION_FAILED"
+ V2ErrorsEnumMetadataOverride V2ErrorsEnum = "METADATA_OVERRIDE"
+ V2ErrorsEnumNotFound V2ErrorsEnum = "NOT_FOUND"
+ V2ErrorsEnumRevertOccurring V2ErrorsEnum = "REVERT_OCCURRING"
+ V2ErrorsEnumAlreadyRevert V2ErrorsEnum = "ALREADY_REVERT"
+ V2ErrorsEnumNoPostings V2ErrorsEnum = "NO_POSTINGS"
+ V2ErrorsEnumLedgerNotFound V2ErrorsEnum = "LEDGER_NOT_FOUND"
+ V2ErrorsEnumImport V2ErrorsEnum = "IMPORT"
+ V2ErrorsEnumTimeout V2ErrorsEnum = "TIMEOUT"
+ V2ErrorsEnumBulkSizeExceeded V2ErrorsEnum = "BULK_SIZE_EXCEEDED"
+ V2ErrorsEnumInterpreterParse V2ErrorsEnum = "INTERPRETER_PARSE"
+ V2ErrorsEnumInterpreterRuntime V2ErrorsEnum = "INTERPRETER_RUNTIME"
+ V2ErrorsEnumLedgerAlreadyExists V2ErrorsEnum = "LEDGER_ALREADY_EXISTS"
)
func (e V2ErrorsEnum) ToPointer() *V2ErrorsEnum {
@@ -68,6 +69,8 @@ func (e *V2ErrorsEnum) UnmarshalJSON(data []byte) error {
case "INTERPRETER_PARSE":
fallthrough
case "INTERPRETER_RUNTIME":
+ fallthrough
+ case "LEDGER_ALREADY_EXISTS":
*e = V2ErrorsEnum(v)
return nil
default:
diff --git a/pkg/client/models/components/v2posttransaction.go b/pkg/client/models/components/v2posttransaction.go
index a4e3c695f..812d3497a 100644
--- a/pkg/client/models/components/v2posttransaction.go
+++ b/pkg/client/models/components/v2posttransaction.go
@@ -8,8 +8,8 @@ import (
)
type V2PostTransactionScript struct {
- Plain string `json:"plain"`
- Vars map[string]any `json:"vars,omitempty"`
+ Plain string `json:"plain"`
+ Vars map[string]string `json:"vars,omitempty"`
}
func (o *V2PostTransactionScript) GetPlain() string {
@@ -19,7 +19,7 @@ func (o *V2PostTransactionScript) GetPlain() string {
return o.Plain
}
-func (o *V2PostTransactionScript) GetVars() map[string]any {
+func (o *V2PostTransactionScript) GetVars() map[string]string {
if o == nil {
return nil
}
diff --git a/test/e2e/api_ledgers_create_test.go b/test/e2e/api_ledgers_create_test.go
index 92181fab3..9aedb0030 100644
--- a/test/e2e/api_ledgers_create_test.go
+++ b/test/e2e/api_ledgers_create_test.go
@@ -80,7 +80,7 @@ var _ = Context("Ledger engine tests", func() {
Ledger: createLedgerRequest.Ledger,
})
Expect(err).NotTo(BeNil())
- Expect(err).To(HaveErrorCode(string(components.V2ErrorsEnumValidation)))
+ Expect(err).To(HaveErrorCode(string(components.V2ErrorsEnumLedgerAlreadyExists)))
})
It("should fail", func() {})
})
diff --git a/test/e2e/api_transactions_create_test.go b/test/e2e/api_transactions_create_test.go
index f7f40cacc..227822224 100644
--- a/test/e2e/api_transactions_create_test.go
+++ b/test/e2e/api_transactions_create_test.go
@@ -332,7 +332,7 @@ var _ = Context("Ledger accounts list API tests", func() {
source = @world
destination = @bob
)`,
- Vars: map[string]interface{}{},
+ Vars: map[string]string{},
},
},
Ledger: "default",
@@ -365,7 +365,7 @@ var _ = Context("Ledger accounts list API tests", func() {
source = @world
destination = @bob
)`,
- Vars: map[string]interface{}{
+ Vars: map[string]string{
"amount": "USD -100",
},
},
@@ -393,7 +393,7 @@ var _ = Context("Ledger accounts list API tests", func() {
Metadata: map[string]string{},
Script: &components.V2PostTransactionScript{
Plain: `XXX`,
- Vars: map[string]interface{}{},
+ Vars: map[string]string{},
},
},
Ledger: "default",
@@ -422,7 +422,7 @@ var _ = Context("Ledger accounts list API tests", func() {
}
set_tx_meta("foo", "bar")
`,
- Vars: map[string]interface{}{
+ Vars: map[string]string{
"amount": "USD 100",
},
},
@@ -449,7 +449,7 @@ var _ = Context("Ledger accounts list API tests", func() {
destination = @bob
)
set_tx_meta("foo", "bar")`,
- Vars: map[string]interface{}{},
+ Vars: map[string]string{},
},
},
Ledger: "default",
@@ -471,7 +471,7 @@ var _ = Context("Ledger accounts list API tests", func() {
source = @world
destination = @bob
)`,
- Vars: map[string]interface{}{},
+ Vars: map[string]string{},
},
},
DryRun: pointer.For(true),
diff --git a/test/performance/benchmark_test.go b/test/performance/benchmark_test.go
index d816b796e..3ac8b0f21 100644
--- a/test/performance/benchmark_test.go
+++ b/test/performance/benchmark_test.go
@@ -156,16 +156,12 @@ func (benchmark *Benchmark) createTransaction(
script string,
vars map[string]string,
) (*ledger.Transaction, error) {
- varsAsMapAny := make(map[string]any)
- for k, v := range vars {
- varsAsMapAny[k] = v
- }
response, err := client.Ledger.V2.CreateTransaction(ctx, operations.V2CreateTransactionRequest{
Ledger: l.Name,
V2PostTransaction: components.V2PostTransaction{
Script: &components.V2PostTransactionScript{
Plain: script,
- Vars: varsAsMapAny,
+ Vars: vars,
},
},
})
diff --git a/tools/generator/cmd/root.go b/tools/generator/cmd/root.go
new file mode 100644
index 000000000..832e97a5c
--- /dev/null
+++ b/tools/generator/cmd/root.go
@@ -0,0 +1,119 @@
+package cmd
+
+import (
+ "errors"
+ "fmt"
+ "github.com/formancehq/go-libs/v2/logging"
+ ledgerclient "github.com/formancehq/ledger/pkg/client"
+ "github.com/formancehq/ledger/pkg/client/models/components"
+ "github.com/formancehq/ledger/pkg/client/models/operations"
+ "github.com/formancehq/ledger/pkg/client/models/sdkerrors"
+ "github.com/formancehq/ledger/pkg/generate"
+ "os"
+ "sync"
+
+ "github.com/spf13/cobra"
+)
+
+var (
+ rootCmd = &cobra.Command{
+ Use: "generator ",
+ Short: "Generate data for a ledger. WARNING: This is an experimental tool.",
+ RunE: run,
+ Args: cobra.ExactArgs(2),
+ }
+ parallelFlag = "parallel"
+ ledgerFlag = "ledger"
+ untilTransactionIDFlag = "until-transaction-id"
+)
+
+func run(cmd *cobra.Command, args []string) error {
+ ledgerUrl := args[0]
+ scriptLocation := args[1]
+
+ fileContent, err := os.ReadFile(scriptLocation)
+ if err != nil {
+ return fmt.Errorf("failed to read file: %w", err)
+ }
+
+ vus, err := cmd.Flags().GetInt(parallelFlag)
+ if err != nil {
+ return fmt.Errorf("failed to get vu: %w", err)
+ }
+
+ ledger, err := cmd.Flags().GetString(ledgerFlag)
+ if err != nil {
+ return fmt.Errorf("failed to get ledger: %w", err)
+ }
+
+ untilTransactionID, err := cmd.Flags().GetInt64(untilTransactionIDFlag)
+ if err != nil {
+ return fmt.Errorf("failed to get untilTransactionID: %w", err)
+ }
+
+ client := ledgerclient.New(ledgerclient.WithServerURL(ledgerUrl))
+
+ _, err = client.Ledger.V2.CreateLedger(cmd.Context(), operations.V2CreateLedgerRequest{
+ Ledger: ledger,
+ })
+ if err != nil {
+ sdkError := &sdkerrors.V2ErrorResponse{}
+ if !errors.As(err, &sdkError) || sdkError.ErrorCode != components.V2ErrorsEnumLedgerAlreadyExists {
+ return fmt.Errorf("failed to create ledger: %w", err)
+ }
+ }
+
+ wg := sync.WaitGroup{}
+ wg.Add(vus)
+
+ for i := 0; i < vus; i++ {
+ generator, err := generate.NewGenerator(string(fileContent))
+ if err != nil {
+ return fmt.Errorf("failed to create generator: %w", err)
+ }
+ go func() {
+ defer wg.Done()
+
+ for {
+ next := generator.Next(i)
+ tx, err := client.Ledger.V2.CreateTransaction(
+ cmd.Context(),
+ operations.V2CreateTransactionRequest{
+ Ledger: ledger,
+ V2PostTransaction: components.V2PostTransaction{
+ Script: &components.V2PostTransactionScript{
+ Plain: next.Script,
+ Vars: next.Variables,
+ },
+ },
+ },
+ )
+ if err != nil {
+ logging.FromContext(cmd.Context()).Errorf("Vu stopped with error: %s", err)
+ return
+ }
+ if untilTransactionID != 0 && tx.V2CreateTransactionResponse.Data.ID.Int64() >= untilTransactionID {
+ return
+ }
+ }
+ }()
+ }
+
+ wg.Wait()
+
+ return nil
+}
+
+func Execute() {
+ err := rootCmd.Execute()
+ if err != nil {
+ os.Exit(1)
+ }
+}
+
+func init() {
+ rootCmd.Flags().IntP(parallelFlag, "p", 1, "Number of parallel users")
+ rootCmd.Flags().StringP(ledgerFlag, "l", "default", "Ledger to feed")
+ rootCmd.Flags().Int64P(untilTransactionIDFlag, "u", 0, "Stop after this transaction ID")
+ rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}
diff --git a/tools/generator/examples/example1.js b/tools/generator/examples/example1.js
new file mode 100644
index 000000000..2517a3c16
--- /dev/null
+++ b/tools/generator/examples/example1.js
@@ -0,0 +1,26 @@
+const script = `vars {
+ account $order
+ account $seller
+}
+send [USD/2 100] (
+ source = @world
+ destination = $order
+)
+send [USD/2 1] (
+ source = $order
+ destination = @fees
+)
+send [USD/2 99] (
+ source = $order
+ destination = $seller
+)`
+
+function next(iteration) {
+ return {
+ script,
+ variables: {
+ order: `orders:${uuid()}`,
+ seller: `sellers:${iteration % 5}`
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/generator/main.go b/tools/generator/main.go
new file mode 100644
index 000000000..9b4def5eb
--- /dev/null
+++ b/tools/generator/main.go
@@ -0,0 +1,7 @@
+package main
+
+import "github.com/formancehq/ledger/tools/generator/cmd"
+
+func main() {
+ cmd.Execute()
+}