diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ac63473c7..41c7d1e14 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,9 +1,9 @@ -/api/ @numary/core -/cmd/ @numary/core -/config/ @numary/core -/core/ @numary/core -/ledger/ @numary/core -*.go @numary/core +/api/ @numary/dev-backend +/cmd/ @numary/dev-backend +/config/ @numary/dev-backend +/core/ @numary/dev-backend +/ledger/ @numary/dev-backend +*.go @numary/dev-backend /.devcontainer @flemzord /.github @flemzord diff --git a/.goreleaser.yml b/.goreleaser.yml index 357ef6f75..8c514a17f 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -111,7 +111,7 @@ changelog: release: prerelease: auto footer: | - **Full Changelog**: https://github.com/numary/ledger/compare/{{ .PreviousTag }}...{{ .Tag }} + **Full Changelog**: https://docs.formance.com/oss/ledger/changelog/{{ .Tag }} ## What to do next? - Read the [documentation](https://docs.numary.com/oss/ledger/get-started/installation) - Join our [Discord server](https://discord.gg/xyHvcbzk4w) @@ -123,7 +123,7 @@ brews: name: numary folder: Formula homepage: https://numary.com - skip_upload: false + skip_upload: 'false' test: | system "#{bin}/numary version" install: | diff --git a/README.md b/README.md index 9c6d71b9f..bd28ab3da 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Numary Ledger [![test](https://github.com/numary/ledger/actions/workflows/main.yml/badge.svg)](https://github.com/numary/ledger/actions/workflows/main.yml) [![goreportcard](https://goreportcard.com/badge/github.com/numary/ledger)](https://goreportcard.com/report/github.com/numary/ledger) [![discord](https://img.shields.io/discord/846686859869814784?label=chat%20@%20discord)](https://discord.gg/xyHvcbzk4w) +# Numary Ledger [![test](https://github.com/numary/ledger/actions/workflows/main.yml/badge.svg)](https://github.com/numary/ledger/actions/workflows/main.yml) [![goreportcard](https://goreportcard.com/badge/github.com/numary/ledger)](https://goreportcard.com/report/github.com/numary/ledger) [![discord](https://img.shields.io/discord/846686859869814784?label=chat%20@%20discord)](https://discord.gg/xyHvcbzk4w) [![codecov](https://codecov.io/gh/numary/ledger/branch/main/graph/badge.svg?token=3PUKLWIKX3)](https://codecov.io/gh/numary/ledger)
diff --git a/pkg/api/controllers/transaction_controller_test.go b/pkg/api/controllers/transaction_controller_test.go index b40b83863..420647999 100644 --- a/pkg/api/controllers/transaction_controller_test.go +++ b/pkg/api/controllers/transaction_controller_test.go @@ -504,9 +504,7 @@ func TestGetTransactions(t *testing.T) { } type transaction struct { - core.TransactionData - ID uint64 `json:"txid"` - Timestamp string `json:"timestamp"` + core.Transaction PreCommitVolumes accountsVolumes `json:"preCommitVolumes,omitempty"` PostCommitVolumes accountsVolumes `json:"postCommitVolumes,omitempty"` } @@ -518,7 +516,9 @@ func TestTransactionsVolumes(t *testing.T) { lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { - var worldAlice int64 = 100 + // Single posting - single asset + + const worldAliceUSD int64 = 100 rsp := internal.PostTransaction(t, api, core.TransactionData{ @@ -526,7 +526,7 @@ func TestTransactionsVolumes(t *testing.T) { { Source: "world", Destination: "alice", - Amount: worldAlice, + Amount: worldAliceUSD, Asset: "USD", }, }, @@ -548,14 +548,14 @@ func TestTransactionsVolumes(t *testing.T) { expPostVolumes := accountsVolumes{ "alice": assetsVolumes{ "USD": core.VolumesWithBalance{ - Input: worldAlice, - Balance: worldAlice, + Input: worldAliceUSD, + Balance: worldAliceUSD, }, }, "world": assetsVolumes{ "USD": core.VolumesWithBalance{ - Output: worldAlice, - Balance: -worldAlice, + Output: worldAliceUSD, + Balance: -worldAliceUSD, }, }, } @@ -571,7 +571,11 @@ func TestTransactionsVolumes(t *testing.T) { assert.Equal(t, expPreVolumes, cursor.Data[0].PreCommitVolumes) assert.Equal(t, expPostVolumes, cursor.Data[0].PostCommitVolumes) - var aliceBob int64 = 93 + prevVolAliceUSD := expPostVolumes["alice"]["USD"] + + // Single posting - single asset + + const aliceBobUSD int64 = 93 rsp = internal.PostTransaction(t, api, core.TransactionData{ @@ -579,7 +583,7 @@ func TestTransactionsVolumes(t *testing.T) { { Source: "alice", Destination: "bob", - Amount: aliceBob, + Amount: aliceBobUSD, Asset: "USD", }, }, @@ -589,10 +593,10 @@ func TestTransactionsVolumes(t *testing.T) { require.True(t, ok) require.Len(t, txs, 1) - prevAlice := expPostVolumes["alice"] - expPreVolumes = accountsVolumes{ - "alice": prevAlice, + "alice": assetsVolumes{ + "USD": prevVolAliceUSD, + }, "bob": assetsVolumes{ "USD": core.VolumesWithBalance{}, }, @@ -601,15 +605,15 @@ func TestTransactionsVolumes(t *testing.T) { expPostVolumes = accountsVolumes{ "alice": assetsVolumes{ "USD": core.VolumesWithBalance{ - Input: prevAlice["USD"].Input, - Output: prevAlice["USD"].Output + aliceBob, - Balance: prevAlice["USD"].Input - prevAlice["USD"].Output - aliceBob, + Input: prevVolAliceUSD.Input, + Output: prevVolAliceUSD.Output + aliceBobUSD, + Balance: prevVolAliceUSD.Input - prevVolAliceUSD.Output - aliceBobUSD, }, }, "bob": assetsVolumes{ "USD": core.VolumesWithBalance{ - Input: aliceBob, - Balance: aliceBob, + Input: aliceBobUSD, + Balance: aliceBobUSD, }, }, } @@ -625,6 +629,162 @@ func TestTransactionsVolumes(t *testing.T) { assert.Equal(t, expPreVolumes, cursor.Data[0].PreCommitVolumes) assert.Equal(t, expPostVolumes, cursor.Data[0].PostCommitVolumes) + prevVolAliceUSD = expPostVolumes["alice"]["USD"] + prevVolBobUSD := expPostVolumes["bob"]["USD"] + + // Multi posting - single asset + + const worldBobEUR int64 = 156 + const bobAliceEUR int64 = 3 + + rsp = internal.PostTransaction(t, api, + core.TransactionData{ + Postings: core.Postings{ + { + Source: "world", + Destination: "bob", + Amount: worldBobEUR, + Asset: "EUR", + }, + { + Source: "bob", + Destination: "alice", + Amount: bobAliceEUR, + Asset: "EUR", + }, + }, + }) + require.Equal(t, http.StatusOK, rsp.Result().StatusCode) + txs, ok = internal.DecodeSingleResponse[[]transaction](t, rsp.Body) + require.True(t, ok) + require.Len(t, txs, 1) + + expPreVolumes = accountsVolumes{ + "alice": assetsVolumes{ + "EUR": core.VolumesWithBalance{}, + }, + "bob": assetsVolumes{ + "EUR": core.VolumesWithBalance{}, + }, + "world": assetsVolumes{ + "EUR": core.VolumesWithBalance{}, + }, + } + + expPostVolumes = accountsVolumes{ + "alice": assetsVolumes{ + "EUR": core.VolumesWithBalance{ + Input: bobAliceEUR, + Output: 0, + Balance: bobAliceEUR, + }, + }, + "bob": assetsVolumes{ + "EUR": core.VolumesWithBalance{ + Input: worldBobEUR, + Output: bobAliceEUR, + Balance: worldBobEUR - bobAliceEUR, + }, + }, + "world": assetsVolumes{ + "EUR": core.VolumesWithBalance{ + Input: 0, + Output: worldBobEUR, + Balance: -worldBobEUR, + }, + }, + } + + assert.Equal(t, expPreVolumes, txs[0].PreCommitVolumes) + assert.Equal(t, expPostVolumes, txs[0].PostCommitVolumes) + + rsp = internal.GetTransactions(api, url.Values{}) + require.Equal(t, http.StatusOK, rsp.Result().StatusCode) + cursor = internal.DecodeCursorResponse[transaction](t, rsp.Body) + require.Len(t, cursor.Data, 3) + + assert.Equal(t, expPreVolumes, cursor.Data[0].PreCommitVolumes) + assert.Equal(t, expPostVolumes, cursor.Data[0].PostCommitVolumes) + + prevVolAliceEUR := expPostVolumes["alice"]["EUR"] + prevVolBobEUR := expPostVolumes["bob"]["EUR"] + + // Multi postings - multi assets + + const bobAliceUSD int64 = 1 + const aliceBobEUR int64 = 2 + + rsp = internal.PostTransaction(t, api, + core.TransactionData{ + Postings: core.Postings{ + { + Source: "bob", + Destination: "alice", + Amount: bobAliceUSD, + Asset: "USD", + }, + { + Source: "alice", + Destination: "bob", + Amount: aliceBobEUR, + Asset: "EUR", + }, + }, + }) + require.Equal(t, http.StatusOK, rsp.Result().StatusCode) + txs, ok = internal.DecodeSingleResponse[[]transaction](t, rsp.Body) + require.True(t, ok) + require.Len(t, txs, 1) + + expPreVolumes = accountsVolumes{ + "alice": assetsVolumes{ + "EUR": prevVolAliceEUR, + "USD": prevVolAliceUSD, + }, + "bob": assetsVolumes{ + "EUR": prevVolBobEUR, + "USD": prevVolBobUSD, + }, + } + + expPostVolumes = accountsVolumes{ + "alice": assetsVolumes{ + "EUR": core.VolumesWithBalance{ + Input: prevVolAliceEUR.Input, + Output: prevVolAliceEUR.Output + aliceBobEUR, + Balance: prevVolAliceEUR.Balance - aliceBobEUR, + }, + "USD": core.VolumesWithBalance{ + Input: prevVolAliceUSD.Input + bobAliceUSD, + Output: prevVolAliceUSD.Output, + Balance: prevVolAliceUSD.Balance + bobAliceUSD, + }, + }, + "bob": assetsVolumes{ + "EUR": core.VolumesWithBalance{ + Input: prevVolBobEUR.Input + aliceBobEUR, + Output: prevVolBobEUR.Output, + Balance: prevVolBobEUR.Balance + aliceBobEUR, + }, + "USD": core.VolumesWithBalance{ + Input: prevVolBobUSD.Input, + Output: prevVolBobUSD.Output + bobAliceUSD, + Balance: prevVolBobUSD.Balance - bobAliceUSD, + }, + }, + } + + assert.Equal(t, expPreVolumes, txs[0].PreCommitVolumes) + assert.Equal(t, expPostVolumes, txs[0].PostCommitVolumes) + + rsp = internal.GetTransactions(api, url.Values{}) + require.Equal(t, http.StatusOK, rsp.Result().StatusCode) + cursor = internal.DecodeCursorResponse[transaction](t, rsp.Body) + require.Len(t, cursor.Data, 4) + + assert.Equal(t, expPreVolumes, cursor.Data[0].PreCommitVolumes) + assert.Equal(t, expPostVolumes, cursor.Data[0].PostCommitVolumes) + return nil }, }) diff --git a/pkg/ledger/process_test.go b/pkg/ledger/process_test.go index db45bb596..a2af971a5 100644 --- a/pkg/ledger/process_test.go +++ b/pkg/ledger/process_test.go @@ -12,7 +12,7 @@ import ( func TestLedger_processTx(t *testing.T) { runOnLedger(func(l *Ledger) { t.Run("multi assets", func(t *testing.T) { - var ( + const ( worldTotoUSD int64 = 43 worldAliceUSD int64 = 98 aliceTotoUSD int64 = 45