diff --git a/example/app/example-app/projections.go b/example/app/example-app/projections.go index 287a7013..b5cc2942 100644 --- a/example/app/example-app/projections.go +++ b/example/app/example-app/projections.go @@ -150,7 +150,7 @@ func InitProjection(name string, params InitProjectionParams) projection_entity. databaseURL := migrationhelper.GenerateDefaultDatabaseURL(name, connString) migrationHelper := github_migrationhelper.NewGithubMigrationHelper(sourceURL, databaseURL) - return account.NewAccount(params.Logger, params.RdbConn, params.CosmosAppClient, migrationHelper) + return account.NewAccount(params.Logger, params.RdbConn, params.AccountAddressPrefix, migrationHelper) case "AccountTransaction": sourceURL := github_migrationhelper.GenerateDefaultSourceURL(name, githubMigrationHelperConfig) databaseURL := migrationhelper.GenerateDefaultDatabaseURL(name, connString) diff --git a/example/go.mod b/example/go.mod index c3a910d1..044d5676 100644 --- a/example/go.mod +++ b/example/go.mod @@ -6,13 +6,10 @@ replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.2-alp replace github.com/rs/zerolog => github.com/rs/zerolog v1.23.0 -require ( - github.com/BurntSushi/toml v0.4.1 - github.com/urfave/cli/v2 v2.3.0 -) +require github.com/urfave/cli/v2 v2.3.0 require ( - github.com/crypto-com/chain-indexing v1.3.0 + github.com/crypto-com/chain-indexing v1.3.1 github.com/ettle/strcase v0.1.1 github.com/valyala/fasthttp v1.17.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/example/go.sum b/example/go.sum index 42de6b74..b5d7ea08 100644 --- a/example/go.sum +++ b/example/go.sum @@ -95,7 +95,6 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= @@ -480,8 +479,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/crypto-com/chain-indexing v1.2.1-0.20220111132225-b2712ba4fe23 h1:Pit1X4i8SXU/pUg60mo3tj/fKM+xYJ5j3TGtBK41D3k= -github.com/crypto-com/chain-indexing v1.2.1-0.20220111132225-b2712ba4fe23/go.mod h1:0KBBR7C2RZDavMpCBKlNtHkMZZ7tyndioZQRWUTD4YI= +github.com/crypto-com/chain-indexing v1.3.1 h1:EaFTbJp8kLZdKhzBqLhKGl1MOeDBe9ljS1Tc8yktyMQ= +github.com/crypto-com/chain-indexing v1.3.1/go.mod h1:ur0Fv7beja9AJxr59ZhxtGKME/wXkhuXh4aPZ7zZzXM= github.com/crypto-org-chain/chain-main/v3 v3.0.0-croeseid h1:YAffq+tYiSqYXgIb11Vc6dvBtgSvRsf2g1wPaMFkQUA= github.com/crypto-org-chain/chain-main/v3 v3.0.0-croeseid/go.mod h1:92Z70bDbsScrWzIHB496p7nXKydtq5am9rqa0fCpbM8= github.com/crypto-org-chain/cronos v0.6.0-testnet h1:iFLwra4QMZ6HgIEB1P0vMCa9pnQTeXjwSuYbH4glxqA= diff --git a/infrastructure/tendermint/httpclient_test.go b/infrastructure/tendermint/httpclient_test.go index 4d1e1af9..189834f9 100644 --- a/infrastructure/tendermint/httpclient_test.go +++ b/infrastructure/tendermint/httpclient_test.go @@ -48,6 +48,7 @@ var _ = Describe("HTTPClient", func() { Height: anyBlockHeight, TxsResults: []usecase_model.BlockResultsTxsResult{}, BeginBlockEvents: []usecase_model.BlockResultsEvent{}, + BlockEvents: []usecase_model.BlockResultsEvent{}, EndBlockEvents: []usecase_model.BlockResultsEvent{}, ValidatorUpdates: []usecase_model.BlockResultsValidatorUpdate{}, ConsensusParamUpdates: usecase_model.BlockResultsConsensusParamUpdates{ @@ -79,7 +80,7 @@ var _ = Describe("HTTPClient", func() { blockResults, err := client.BlockResults(anyBlockHeight) Expect(err).To(BeNil()) - expected := "{\"height\":367216,\"txsResults\":[{\"code\":0,\"data\":\"\\n\\n\\n\\u0008delegate\",\"log\":[{\"msgIndex\":0,\"events\":[{\"type\":\"delegate\",\"attributes\":[{\"key\":\"validator\",\"value\":\"tcrocncl1pm27djcs5djxjsxw3unrkv3m3jtxdexktw5epu\"},{\"key\":\"amount\",\"value\":\"19302674761\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"delegate\"},{\"key\":\"sender\",\"value\":\"tcro1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8339p4l\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"tcro1pm27djcs5djxjsxw3unrkv3m3jtxdexk73hqel\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"tcro1pm27djcs5djxjsxw3unrkv3m3jtxdexk73hqel\"},{\"key\":\"sender\",\"value\":\"tcro1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8339p4l\"},{\"key\":\"amount\",\"value\":\"1913979901basetcro\"}]}]}],\"rawLog\":\"[{\\\"events\\\":[{\\\"type\\\":\\\"delegate\\\",\\\"attributes\\\":[{\\\"key\\\":\\\"validator\\\",\\\"value\\\":\\\"tcrocncl1pm27djcs5djxjsxw3unrkv3m3jtxdexktw5epu\\\"},{\\\"key\\\":\\\"amount\\\",\\\"value\\\":\\\"19302674761\\\"}]},{\\\"type\\\":\\\"message\\\",\\\"attributes\\\":[{\\\"key\\\":\\\"action\\\",\\\"value\\\":\\\"delegate\\\"},{\\\"key\\\":\\\"sender\\\",\\\"value\\\":\\\"tcro1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8339p4l\\\"},{\\\"key\\\":\\\"module\\\",\\\"value\\\":\\\"staking\\\"},{\\\"key\\\":\\\"sender\\\",\\\"value\\\":\\\"tcro1pm27djcs5djxjsxw3unrkv3m3jtxdexk73hqel\\\"}]},{\\\"type\\\":\\\"transfer\\\",\\\"attributes\\\":[{\\\"key\\\":\\\"recipient\\\",\\\"value\\\":\\\"tcro1pm27djcs5djxjsxw3unrkv3m3jtxdexk73hqel\\\"},{\\\"key\\\":\\\"sender\\\",\\\"value\\\":\\\"tcro1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8339p4l\\\"},{\\\"key\\\":\\\"amount\\\",\\\"value\\\":\\\"1913979901basetcro\\\"}]}]}]\",\"info\":\"\",\"gasWanted\":\"200000\",\"gasUsed\":\"143179\",\"events\":[{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"tcro17xpfvakm2amg962yls6f84z3kell8c5lxhzaha\"},{\"key\":\"sender\",\"value\":\"tcro1pm27djcs5djxjsxw3unrkv3m3jtxdexk73hqel\"},{\"key\":\"amount\",\"value\":\"20000basetcro\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"sender\",\"value\":\"tcro1pm27djcs5djxjsxw3unrkv3m3jtxdexk73hqel\"}]}],\"codespace\":\"\"},{\"code\":0,\"data\":\"\\n\\n\\n\\u0008delegate\",\"log\":[{\"msgIndex\":0,\"events\":[{\"type\":\"delegate\",\"attributes\":[{\"key\":\"validator\",\"value\":\"tcrocncl10gsqs8jzdlrem80shp0x6wx0jw7qu7m8cd29y5\"},{\"key\":\"amount\",\"value\":\"17338013566\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"delegate\"},{\"key\":\"sender\",\"value\":\"tcro1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8339p4l\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"tcro10gsqs8jzdlrem80shp0x6wx0jw7qu7m8djfuuh\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"tcro10gsqs8jzdlrem80shp0x6wx0jw7qu7m8djfuuh\"},{\"key\":\"sender\",\"value\":\"tcro1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8339p4l\"},{\"key\":\"amount\",\"value\":\"2310941249basetcro\"}]}]}],\"rawLog\":\"[{\\\"events\\\":[{\\\"type\\\":\\\"delegate\\\",\\\"attributes\\\":[{\\\"key\\\":\\\"validator\\\",\\\"value\\\":\\\"tcrocncl10gsqs8jzdlrem80shp0x6wx0jw7qu7m8cd29y5\\\"},{\\\"key\\\":\\\"amount\\\",\\\"value\\\":\\\"17338013566\\\"}]},{\\\"type\\\":\\\"message\\\",\\\"attributes\\\":[{\\\"key\\\":\\\"action\\\",\\\"value\\\":\\\"delegate\\\"},{\\\"key\\\":\\\"sender\\\",\\\"value\\\":\\\"tcro1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8339p4l\\\"},{\\\"key\\\":\\\"module\\\",\\\"value\\\":\\\"staking\\\"},{\\\"key\\\":\\\"sender\\\",\\\"value\\\":\\\"tcro10gsqs8jzdlrem80shp0x6wx0jw7qu7m8djfuuh\\\"}]},{\\\"type\\\":\\\"transfer\\\",\\\"attributes\\\":[{\\\"key\\\":\\\"recipient\\\",\\\"value\\\":\\\"tcro10gsqs8jzdlrem80shp0x6wx0jw7qu7m8djfuuh\\\"},{\\\"key\\\":\\\"sender\\\",\\\"value\\\":\\\"tcro1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8339p4l\\\"},{\\\"key\\\":\\\"amount\\\",\\\"value\\\":\\\"2310941249basetcro\\\"}]}]}]\",\"info\":\"\",\"gasWanted\":\"200000\",\"gasUsed\":\"143737\",\"events\":[{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"tcro17xpfvakm2amg962yls6f84z3kell8c5lxhzaha\"},{\"key\":\"sender\",\"value\":\"tcro10gsqs8jzdlrem80shp0x6wx0jw7qu7m8djfuuh\"},{\"key\":\"amount\",\"value\":\"20000basetcro\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"sender\",\"value\":\"tcro10gsqs8jzdlrem80shp0x6wx0jw7qu7m8djfuuh\"}]}],\"codespace\":\"\"}],\"beginBlockEvents\":[{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"tcro17xpfvakm2amg962yls6f84z3kell8c5lxhzaha\"},{\"key\":\"sender\",\"value\":\"tcro1m3h30wlvsf8llruxtpukdvsy0km2kum87lx9mq\"},{\"key\":\"amount\",\"value\":\"17449528321basetcro\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"sender\",\"value\":\"tcro1m3h30wlvsf8llruxtpukdvsy0km2kum87lx9mq\"}]},{\"type\":\"mint\",\"attributes\":[{\"key\":\"bonded_ratio\",\"value\":\"0.000809196054376644\"},{\"key\":\"inflation\",\"value\":\"0.013755821936855184\"},{\"key\":\"annual_provisions\",\"value\":\"110133046994204576.138016526579386288\"},{\"key\":\"amount\",\"value\":\"17449528321\"}]}],\"endBlockEvents\":[{\"type\":\"commission\",\"attributes\":[{\"key\":\"amount\",\"value\":\"87247841.605000000000000000basetcro\"},{\"key\":\"validator\",\"value\":\"tcrocncl18p07yvmphymscz6tl4a7zmh93g0k6vy72ww4s4\"}]},{\"type\":\"rewards\",\"attributes\":[{\"key\":\"amount\",\"value\":\"872478416.050000000000000000basetcro\"},{\"key\":\"validator\",\"value\":\"tcrocncl18p07yvmphymscz6tl4a7zmh93g0k6vy72ww4s4\"}]}],\"validatorUpdates\":[{\"pubkey\":{\"type\":\"tendermint.crypto.PublicKey_Ed25519\",\"pubkey\":\"SE5zeTjcYPXVrfcOva61QWokSZFfQu2h316fR6bB2dY=\"},\"address\":\"CA721C3A05F500838DDD1B16F4E2D2D09E463218\",\"power\":138525202},{\"pubkey\":{\"type\":\"tendermint.crypto.PublicKey_Ed25519\",\"pubkey\":\"Epmo3U6yXlxSDQzWZ8yBPOMHw2R85lc26RK98Rlo0oM=\"},\"address\":\"E067FCE33F7FDBD0CE4872F8E240A7AD6E654726\",\"power\":112904113}],\"consensusParamUpdates\":{\"block\":{\"maxBytes\":\"22020096\",\"maxGas\":\"-1\"},\"evidence\":{\"maxAgeNumBlocks\":\"100000\",\"maxAgeDuration\":\"172800000000000\",\"maxBytes\":\"\"},\"validator\":{\"pubKeyTypes\":[\"ed25519\"]}}}" + expected := "{\"height\":367216,\"txsResults\":[{\"code\":0,\"data\":\"\\n\\n\\n\\u0008delegate\",\"log\":[{\"msgIndex\":0,\"events\":[{\"type\":\"delegate\",\"attributes\":[{\"key\":\"validator\",\"value\":\"tcrocncl1pm27djcs5djxjsxw3unrkv3m3jtxdexktw5epu\"},{\"key\":\"amount\",\"value\":\"19302674761\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"delegate\"},{\"key\":\"sender\",\"value\":\"tcro1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8339p4l\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"tcro1pm27djcs5djxjsxw3unrkv3m3jtxdexk73hqel\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"tcro1pm27djcs5djxjsxw3unrkv3m3jtxdexk73hqel\"},{\"key\":\"sender\",\"value\":\"tcro1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8339p4l\"},{\"key\":\"amount\",\"value\":\"1913979901basetcro\"}]}]}],\"rawLog\":\"[{\\\"events\\\":[{\\\"type\\\":\\\"delegate\\\",\\\"attributes\\\":[{\\\"key\\\":\\\"validator\\\",\\\"value\\\":\\\"tcrocncl1pm27djcs5djxjsxw3unrkv3m3jtxdexktw5epu\\\"},{\\\"key\\\":\\\"amount\\\",\\\"value\\\":\\\"19302674761\\\"}]},{\\\"type\\\":\\\"message\\\",\\\"attributes\\\":[{\\\"key\\\":\\\"action\\\",\\\"value\\\":\\\"delegate\\\"},{\\\"key\\\":\\\"sender\\\",\\\"value\\\":\\\"tcro1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8339p4l\\\"},{\\\"key\\\":\\\"module\\\",\\\"value\\\":\\\"staking\\\"},{\\\"key\\\":\\\"sender\\\",\\\"value\\\":\\\"tcro1pm27djcs5djxjsxw3unrkv3m3jtxdexk73hqel\\\"}]},{\\\"type\\\":\\\"transfer\\\",\\\"attributes\\\":[{\\\"key\\\":\\\"recipient\\\",\\\"value\\\":\\\"tcro1pm27djcs5djxjsxw3unrkv3m3jtxdexk73hqel\\\"},{\\\"key\\\":\\\"sender\\\",\\\"value\\\":\\\"tcro1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8339p4l\\\"},{\\\"key\\\":\\\"amount\\\",\\\"value\\\":\\\"1913979901basetcro\\\"}]}]}]\",\"info\":\"\",\"gasWanted\":\"200000\",\"gasUsed\":\"143179\",\"events\":[{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"tcro17xpfvakm2amg962yls6f84z3kell8c5lxhzaha\"},{\"key\":\"sender\",\"value\":\"tcro1pm27djcs5djxjsxw3unrkv3m3jtxdexk73hqel\"},{\"key\":\"amount\",\"value\":\"20000basetcro\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"sender\",\"value\":\"tcro1pm27djcs5djxjsxw3unrkv3m3jtxdexk73hqel\"}]}],\"codespace\":\"\"},{\"code\":0,\"data\":\"\\n\\n\\n\\u0008delegate\",\"log\":[{\"msgIndex\":0,\"events\":[{\"type\":\"delegate\",\"attributes\":[{\"key\":\"validator\",\"value\":\"tcrocncl10gsqs8jzdlrem80shp0x6wx0jw7qu7m8cd29y5\"},{\"key\":\"amount\",\"value\":\"17338013566\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"delegate\"},{\"key\":\"sender\",\"value\":\"tcro1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8339p4l\"},{\"key\":\"module\",\"value\":\"staking\"},{\"key\":\"sender\",\"value\":\"tcro10gsqs8jzdlrem80shp0x6wx0jw7qu7m8djfuuh\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"tcro10gsqs8jzdlrem80shp0x6wx0jw7qu7m8djfuuh\"},{\"key\":\"sender\",\"value\":\"tcro1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8339p4l\"},{\"key\":\"amount\",\"value\":\"2310941249basetcro\"}]}]}],\"rawLog\":\"[{\\\"events\\\":[{\\\"type\\\":\\\"delegate\\\",\\\"attributes\\\":[{\\\"key\\\":\\\"validator\\\",\\\"value\\\":\\\"tcrocncl10gsqs8jzdlrem80shp0x6wx0jw7qu7m8cd29y5\\\"},{\\\"key\\\":\\\"amount\\\",\\\"value\\\":\\\"17338013566\\\"}]},{\\\"type\\\":\\\"message\\\",\\\"attributes\\\":[{\\\"key\\\":\\\"action\\\",\\\"value\\\":\\\"delegate\\\"},{\\\"key\\\":\\\"sender\\\",\\\"value\\\":\\\"tcro1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8339p4l\\\"},{\\\"key\\\":\\\"module\\\",\\\"value\\\":\\\"staking\\\"},{\\\"key\\\":\\\"sender\\\",\\\"value\\\":\\\"tcro10gsqs8jzdlrem80shp0x6wx0jw7qu7m8djfuuh\\\"}]},{\\\"type\\\":\\\"transfer\\\",\\\"attributes\\\":[{\\\"key\\\":\\\"recipient\\\",\\\"value\\\":\\\"tcro10gsqs8jzdlrem80shp0x6wx0jw7qu7m8djfuuh\\\"},{\\\"key\\\":\\\"sender\\\",\\\"value\\\":\\\"tcro1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8339p4l\\\"},{\\\"key\\\":\\\"amount\\\",\\\"value\\\":\\\"2310941249basetcro\\\"}]}]}]\",\"info\":\"\",\"gasWanted\":\"200000\",\"gasUsed\":\"143737\",\"events\":[{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"tcro17xpfvakm2amg962yls6f84z3kell8c5lxhzaha\"},{\"key\":\"sender\",\"value\":\"tcro10gsqs8jzdlrem80shp0x6wx0jw7qu7m8djfuuh\"},{\"key\":\"amount\",\"value\":\"20000basetcro\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"sender\",\"value\":\"tcro10gsqs8jzdlrem80shp0x6wx0jw7qu7m8djfuuh\"}]}],\"codespace\":\"\"}],\"blockEvents\":[],\"beginBlockEvents\":[{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"tcro17xpfvakm2amg962yls6f84z3kell8c5lxhzaha\"},{\"key\":\"sender\",\"value\":\"tcro1m3h30wlvsf8llruxtpukdvsy0km2kum87lx9mq\"},{\"key\":\"amount\",\"value\":\"17449528321basetcro\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"sender\",\"value\":\"tcro1m3h30wlvsf8llruxtpukdvsy0km2kum87lx9mq\"}]},{\"type\":\"mint\",\"attributes\":[{\"key\":\"bonded_ratio\",\"value\":\"0.000809196054376644\"},{\"key\":\"inflation\",\"value\":\"0.013755821936855184\"},{\"key\":\"annual_provisions\",\"value\":\"110133046994204576.138016526579386288\"},{\"key\":\"amount\",\"value\":\"17449528321\"}]}],\"endBlockEvents\":[{\"type\":\"commission\",\"attributes\":[{\"key\":\"amount\",\"value\":\"87247841.605000000000000000basetcro\"},{\"key\":\"validator\",\"value\":\"tcrocncl18p07yvmphymscz6tl4a7zmh93g0k6vy72ww4s4\"}]},{\"type\":\"rewards\",\"attributes\":[{\"key\":\"amount\",\"value\":\"872478416.050000000000000000basetcro\"},{\"key\":\"validator\",\"value\":\"tcrocncl18p07yvmphymscz6tl4a7zmh93g0k6vy72ww4s4\"}]}],\"validatorUpdates\":[{\"pubkey\":{\"type\":\"tendermint.crypto.PublicKey_Ed25519\",\"pubkey\":\"SE5zeTjcYPXVrfcOva61QWokSZFfQu2h316fR6bB2dY=\"},\"address\":\"CA721C3A05F500838DDD1B16F4E2D2D09E463218\",\"power\":138525202},{\"pubkey\":{\"type\":\"tendermint.crypto.PublicKey_Ed25519\",\"pubkey\":\"Epmo3U6yXlxSDQzWZ8yBPOMHw2R85lc26RK98Rlo0oM=\"},\"address\":\"E067FCE33F7FDBD0CE4872F8E240A7AD6E654726\",\"power\":112904113}],\"consensusParamUpdates\":{\"block\":{\"maxBytes\":\"22020096\",\"maxGas\":\"-1\"},\"evidence\":{\"maxAgeNumBlocks\":\"100000\",\"maxAgeDuration\":\"172800000000000\",\"maxBytes\":\"\"},\"validator\":{\"pubKeyTypes\":[\"ed25519\"]}}}" Expect(jsoniter.MarshalToString(blockResults)).To(Equal(expected)) }) @@ -96,7 +97,7 @@ var _ = Describe("HTTPClient", func() { blockResults, err := client.BlockResults(anyBlockHeight) Expect(err).To(BeNil()) - Expect(jsoniter.MarshalToString(blockResults)).To(Equal("{\"height\":4794,\"txsResults\":[],\"beginBlockEvents\":[],\"endBlockEvents\":[],\"validatorUpdates\":[{\"pubkey\":{\"type\":\"tendermint.crypto.PublicKey_Ed25519\",\"pubkey\":\"CpCz+c19SHaNWW31P+7blzyHo0sQMn4uk8gIej+pXW8=\"},\"address\":\"02D50170AFB2B3718477AA607469BEA96C80CE88\",\"power\":null}],\"consensusParamUpdates\":{\"block\":{\"maxBytes\":\"22020096\",\"maxGas\":\"-1\"},\"evidence\":{\"maxAgeNumBlocks\":\"100000\",\"maxAgeDuration\":\"172800000000000\",\"maxBytes\":\"\"},\"validator\":{\"pubKeyTypes\":[\"ed25519\"]}}}")) + Expect(jsoniter.MarshalToString(blockResults)).To(Equal("{\"height\":4794,\"txsResults\":[],\"blockEvents\":[],\"beginBlockEvents\":[],\"endBlockEvents\":[],\"validatorUpdates\":[{\"pubkey\":{\"type\":\"tendermint.crypto.PublicKey_Ed25519\",\"pubkey\":\"CpCz+c19SHaNWW31P+7blzyHo0sQMn4uk8gIej+pXW8=\"},\"address\":\"02D50170AFB2B3718477AA607469BEA96C80CE88\",\"power\":null}],\"consensusParamUpdates\":{\"block\":{\"maxBytes\":\"22020096\",\"maxGas\":\"-1\"},\"evidence\":{\"maxAgeNumBlocks\":\"100000\",\"maxAgeDuration\":\"172800000000000\",\"maxBytes\":\"\"},\"validator\":{\"pubKeyTypes\":[\"ed25519\"]}}}")) }) }) diff --git a/infrastructure/tendermint/parser.go b/infrastructure/tendermint/parser.go index 138872b0..bcf315db 100644 --- a/infrastructure/tendermint/parser.go +++ b/infrastructure/tendermint/parser.go @@ -100,6 +100,7 @@ func ParseBlockResultsResp(rawRespReader io.Reader) (*model.BlockResults, error) return &model.BlockResults{ Height: int64(height), TxsResults: txsResults, + BlockEvents: parseBlockResultsEvents(rawBlockResults.BlockEvents), BeginBlockEvents: parseBlockResultsEvents(rawBlockResults.BeginBlockEvents), EndBlockEvents: parseBlockResultsEvents(rawBlockResults.EndBlockEvents), ValidatorUpdates: parseBlockResultsValidatorUpdates(rawBlockResults.ValidatorUpdates), diff --git a/infrastructure/tendermint/rawblockresults.go b/infrastructure/tendermint/rawblockresults.go index a04e7e1b..f668b400 100644 --- a/infrastructure/tendermint/rawblockresults.go +++ b/infrastructure/tendermint/rawblockresults.go @@ -3,6 +3,7 @@ package tendermint type RawBlockResults struct { Height string `json:"height"` TxsResults []RawBlockResultsTxsResult `json:"txs_results"` + BlockEvents []RawBlockResultsEvent `json:"events"` BeginBlockEvents []RawBlockResultsEvent `json:"begin_block_events"` EndBlockEvents []RawBlockResultsEvent `json:"end_block_events"` ValidatorUpdates []RawBlockResultsValidatorUpdate `json:"validator_updates"` diff --git a/projection/account/account.go b/projection/account/account.go index 9a1be793..1b9075ae 100644 --- a/projection/account/account.go +++ b/projection/account/account.go @@ -2,12 +2,15 @@ package account import ( "fmt" + "strconv" + "github.com/crypto-com/chain-indexing/external/tmcosmosutils" + "github.com/crypto-com/chain-indexing/internal/base64" + "github.com/crypto-com/chain-indexing/usecase/model" _ "github.com/golang-migrate/migrate/v4/database/postgres" _ "github.com/golang-migrate/migrate/v4/source/file" _ "github.com/golang-migrate/migrate/v4/source/github" - cosmosapp_interface "github.com/crypto-com/chain-indexing/appinterface/cosmosapp" "github.com/crypto-com/chain-indexing/appinterface/projection/rdbprojectionbase" "github.com/crypto-com/chain-indexing/appinterface/rdb" event_entity "github.com/crypto-com/chain-indexing/entity/event" @@ -22,9 +25,10 @@ import ( type Account struct { *rdbprojectionbase.Base + accountAddressPrefix string + rdbConn rdb.Conn logger applogger.Logger - cosmosClient cosmosapp_interface.Client // cosmos light client deamon port : 1317 (default) migrationHelper migrationhelper.MigrationHelper } @@ -32,7 +36,7 @@ type Account struct { func NewAccount( logger applogger.Logger, rdbConn rdb.Conn, - cosmosClient cosmosapp_interface.Client, + accountAddressPrefix string, migrationHelper migrationhelper.MigrationHelper, ) *Account { return &Account{ @@ -45,9 +49,10 @@ func NewAccount( }, ), + accountAddressPrefix, + rdbConn, logger, - cosmosClient, migrationHelper, } @@ -60,8 +65,12 @@ var ( func (_ *Account) GetEventsToListen() []string { return []string{ - // TODO: Genesis account - event_usecase.ACCOUNT_TRANSFERRED, + event_usecase.GENESIS_CREATED, + event_usecase.COIN_SPENT, + event_usecase.COIN_RECEIVED, + event_usecase.COIN_MINT, + event_usecase.COIN_BURN, + event_usecase.TRANSACTION_FAILED, } } @@ -90,9 +99,99 @@ func (projection *Account) HandleEvents(height int64, events []event_entity.Even accountsView := NewAccountsView(rdbTxHandle) for _, event := range events { - if accountCreatedEvent, ok := event.(*event_usecase.AccountTransferred); ok { - if handleErr := projection.handleAccountCreatedEvent(accountsView, accountCreatedEvent); handleErr != nil { - return fmt.Errorf("error handling AccountCreatedEvent: %v", handleErr) + if genesisCreatedEvent, ok := event.(*event_usecase.GenesisCreated); ok { + if handleErr := projection.handleGenesisCreatedEvent(accountsView, genesisCreatedEvent); handleErr != nil { + return fmt.Errorf("error handling GenesisCreatedEvent: %v", handleErr) + } + } else if coinSpentEvent, ok := event.(*event_usecase.CoinSpent); ok { + if handleErr := projection.decrementUsableBalance(accountsView, coinSpentEvent.Address, coinSpentEvent.Amount); handleErr != nil { + return fmt.Errorf("error handling CoinSpentEvent: %v", handleErr) + } + if handleErr := projection.addAccountEvent( + accountsView, + coinSpentEvent.Address, + coinSpentEvent, + coinSpentEvent.EventName, + coinSpentEvent.BlockHeight, + ); handleErr != nil { + return fmt.Errorf("error handling CoinSpentEvent: %v", handleErr) + } + } else if coinReceivedEvent, ok := event.(*event_usecase.CoinReceived); ok { + if handleErr := projection.incrementUsableBalance(accountsView, coinReceivedEvent.Address, coinReceivedEvent.Amount); handleErr != nil { + return fmt.Errorf("error handling CoinReceivedEvent: %v", handleErr) + } + + if handleErr := projection.addAccountEvent( + accountsView, + coinReceivedEvent.Address, + coinReceivedEvent, + coinReceivedEvent.EventName, + coinReceivedEvent.BlockHeight, + ); handleErr != nil { + return fmt.Errorf("error handling CoinReceivedEvent: %v", handleErr) + } + } else if coinMintEvent, ok := event.(*event_usecase.CoinMint); ok { + if handleErr := projection.incrementUsableBalance(accountsView, coinMintEvent.Address, coinMintEvent.Amount); handleErr != nil { + return fmt.Errorf("error handling coinMintEvent: %v", handleErr) + } + if handleErr := projection.addAccountEvent( + accountsView, + coinMintEvent.Address, + coinMintEvent, + coinMintEvent.EventName, + coinMintEvent.BlockHeight, + ); handleErr != nil { + return fmt.Errorf("error handling coinMintEvent: %v", handleErr) + } + } else if coinBurnEvent, ok := event.(*event_usecase.CoinBurn); ok { + if handleErr := projection.decrementUsableBalance(accountsView, coinBurnEvent.Address, coinBurnEvent.Amount); handleErr != nil { + return fmt.Errorf("error handling coinBurnEvent: %v", handleErr) + } + if handleErr := projection.addAccountEvent( + accountsView, + coinBurnEvent.Address, + coinBurnEvent, + coinBurnEvent.EventName, + coinBurnEvent.BlockHeight, + ); handleErr != nil { + return fmt.Errorf("error handling coinBurnEvent: %v", handleErr) + } + } else if transactionFailedEvent, ok := event.(*event_usecase.TransactionFailed); ok { + feePayer := "" + if transactionFailedEvent.FeeGranter != "" { + // if set, the fee payer (either the first signer or the value of the payer field) requests that a fee grant be used + // to pay fees instead of the fee payer's own balance. If an appropriate fee grant does not exist or the chain does + // not support fee grants, this will fail + feePayer = transactionFailedEvent.FeeGranter + } else if transactionFailedEvent.FeePayer != "" { + // if unset, the first signer is responsible for paying the fees. If set, the specified account must pay the fees. + // the payer must be a tx signer (and thus have signed this field in AuthInfo). + feePayer = transactionFailedEvent.FeePayer + } else { + signers := projection.ParseSenderAddresses(transactionFailedEvent.Signers) + feePayer = signers[0] + } + if feePayer != "" { + for i := range transactionFailedEvent.Fee { + if handleErr := projection.decrementUsableBalance( + accountsView, + feePayer, + coin.Coins{transactionFailedEvent.Fee[i]}, + ); handleErr != nil { + return fmt.Errorf("error handling TransactionFaileddEvent: %v", handleErr) + } + if handleErr := projection.addAccountEvent( + accountsView, + feePayer, + transactionFailedEvent, + transactionFailedEvent.EventName, + transactionFailedEvent.BlockHeight, + ); handleErr != nil { + return fmt.Errorf("error handling TransactionFailedEvent: %v", handleErr) + } + } + } else { + fmt.Errorf("empty fee payer on handling event: %v", transactionFailedEvent) } } } @@ -109,72 +208,102 @@ func (projection *Account) HandleEvents(height int64, events []event_entity.Even return nil } -func (projection *Account) handleAccountCreatedEvent(accountsView account_view.Accounts, event *event_usecase.AccountTransferred) error { +func (projection *Account) handleGenesisCreatedEvent(accountsView account_view.Accounts, event *event_usecase.GenesisCreated) error { - recipienterr := projection.writeAccountInfo(accountsView, event.Recipient) - if recipienterr != nil { - return recipienterr - } + for _, account := range event.Genesis.AppState.Bank.Balances { + for _, accountBalance := range account.Coins { + amount, err := strconv.ParseInt(accountBalance.Amount, 10, 0) + if err != nil { + return err + } + if err := accountsView.IncrementUsableBalance( + account.Address, accountBalance.Denom, amount, + ); err != nil { + return err + } - sendererr := projection.writeAccountInfo(accountsView, event.Sender) - if sendererr != nil { - return sendererr + if handleErr := projection.addAccountEvent( + accountsView, + account.Address, + event, + event.EventName, + event.BlockHeight, + ); handleErr != nil { + return fmt.Errorf("error handling accounts in GenesisCreatedEvent: %v", handleErr) + } + } } return nil } -func (projection *Account) getAccountInfo(address string) (*cosmosapp_interface.Account, error) { - var accountInfo, accountInfoError = projection.cosmosClient.Account(address) - if accountInfoError != nil { - return nil, accountInfoError +func (projection *Account) incrementUsableBalance(accountsView account_view.Accounts, address string, amount coin.Coins) error { + for _, increment := range amount { + if err := accountsView.IncrementUsableBalance( + address, increment.Denom, increment.Amount.Int64(), + ); err != nil { + return err + } } - return accountInfo, nil + return nil } -func (projection *Account) getAccountBalances(targetAddress string) (coin.Coins, error) { - var balanceInfo, balanceInfoError = projection.cosmosClient.Balances(targetAddress) - if balanceInfoError != nil { - return nil, balanceInfoError +func (projection *Account) decrementUsableBalance(accountsView account_view.Accounts, address string, amount coin.Coins) error { + for _, decrement := range amount { + if err := accountsView.DecrementUsableBalance( + address, decrement.Denom, decrement.Amount.Int64(), + ); err != nil { + return err + } } - return balanceInfo, nil + return nil } -func (projection *Account) writeAccountInfo(accountsView account_view.Accounts, address string) error { - accountInfo, err := projection.getAccountInfo(address) - if err != nil { +func (projection *Account) addAccountEvent( + accountsView account_view.Accounts, + address string, + data interface{}, + eventType string, + blockHeight int64, +) error { + + if err := accountsView.InsertAccountEvent( + account_view.AccountEvent{ + Address: address, + Type: eventType, + Data: data, + BlockHeight: blockHeight, + }, + ); err != nil { return err } - accountType := accountInfo.Type - var name *string - if accountInfo.Type == cosmosapp_interface.ACCOUNT_MODULE { - name = &accountInfo.MaybeModuleAccount.Name - } - var pubkey *string - if accountInfo.MaybePubkey != nil { - pubkey = &accountInfo.MaybePubkey.Key - } - accountNumber := accountInfo.AccountNumber - sequenceNumber := accountInfo.Sequence + return nil +} - balances, err := projection.getAccountBalances(address) - if err != nil { - return err - } - if err := accountsView.Upsert(&account_view.AccountRow{ - Type: accountType, - Address: address, - MaybeName: name, - MaybePubkey: pubkey, - AccountNumber: accountNumber, - SequenceNumber: sequenceNumber, - Balance: balances, - }); err != nil { - return err +func (projection *Account) ParseSenderAddresses(senders []model.TransactionSigner) []string { + addresses := make([]string, 0, len(senders)) + for _, sender := range senders { + var address string + if sender.IsMultiSig { + addrPubKeys := make([][]byte, 0, len(sender.Pubkeys)) + for _, pubKey := range sender.Pubkeys { + rawPubKey := base64.MustDecodeString(pubKey) + addrPubKeys = append(addrPubKeys, rawPubKey) + } + address = tmcosmosutils.MustMultiSigAddressFromPubKeys( + projection.accountAddressPrefix, + addrPubKeys, + *sender.MaybeThreshold, + false, + ) + } else { + pubKey := base64.MustDecodeString(sender.Pubkeys[0]) + address = tmcosmosutils.MustAccountAddressFromPubKey(projection.accountAddressPrefix, pubKey) + } + addresses = append(addresses, address) } - - return nil + return addresses } diff --git a/projection/account/account_test.go b/projection/account/account_test.go index 26964811..c79bf4a3 100644 --- a/projection/account/account_test.go +++ b/projection/account/account_test.go @@ -2,6 +2,7 @@ package account_test import ( "fmt" + "strconv" "testing" sq "github.com/Masterminds/squirrel" @@ -14,6 +15,8 @@ import ( account_view "github.com/crypto-com/chain-indexing/projection/account/view" "github.com/crypto-com/chain-indexing/usecase/coin" event_usecase "github.com/crypto-com/chain-indexing/usecase/event" + "github.com/crypto-com/chain-indexing/usecase/model" + "github.com/crypto-com/chain-indexing/usecase/model/genesis" "github.com/stretchr/testify/assert" testify_mock "github.com/stretchr/testify/mock" ) @@ -22,7 +25,7 @@ func NewAccountProjection(rdbConn rdb.Conn, client cosmosapp.Client) *account.Ac return account.NewAccount( nil, rdbConn, - client, + "prefix", nil, ) } @@ -61,69 +64,221 @@ func TestAccount_HandleEvents(t *testing.T) { MockFunc func(mockClient *cosmosapp.MockClient) []*testify_mock.Mock }{ { - Name: "HandleAccountTransferred", + Name: "HandleGenesisCreatedEvent", Events: []entity_event.Event{ - &event_usecase.AccountTransferred{ + &event_usecase.GenesisCreated{ Base: entity_event.NewBase(entity_event.BaseParams{ - Name: event_usecase.ACCOUNT_TRANSFERRED, + Name: event_usecase.GENESIS_CREATED, Version: 1, - BlockHeight: 1, + BlockHeight: 0, }), - Sender: "Sender", - Recipient: "Recipient", - Amount: coin.Coins{}, + Genesis: genesis.Genesis{ + GenesisTime: "", + ChainID: "", + InitialHeight: "", + ConsensusParams: genesis.ConsensusParams{}, + AppHash: "", + AppState: genesis.AppState{ + Auth: genesis.Auth{}, + Bank: genesis.Bank{ + Params: genesis.BankParams{}, + Balances: []genesis.Balance{ + { + Address: "Address", + Coins: []genesis.MinDeposit{ + { + Denom: "Denom", + Amount: strconv.Itoa(10), + }, + }, + }, + }, + Supply: nil, + DenomMetadata: nil, + }, + Capability: genesis.Capability{}, + Chainmain: genesis.Chainmain{}, + Distribution: genesis.Distribution{}, + Evidence: genesis.AppStateEvidence{}, + Genutil: genesis.Genutil{}, + Gov: genesis.Gov{}, + Ibc: genesis.Ibc{}, + Mint: genesis.Mint{}, + Params: nil, + Slashing: genesis.Slashing{}, + Staking: genesis.Staking{}, + Supply: genesis.Supply{}, + Transfer: genesis.Transfer{}, + Upgrade: genesis.Upgrade{}, + Vesting: genesis.Vesting{}, + }, + Validators: nil, + }, }, }, MockFunc: func(mockClient *cosmosapp.MockClient) (mocks []*testify_mock.Mock) { - mockClient.On("Account", "Recipient").Return( - &cosmosapp.Account{ - Type: "AccountType", - Address: "Recipient", - MaybePubkey: &cosmosapp.PubKey{ - Type: "PubKeyType", - Key: "Key", - }, - AccountNumber: "AccountNumber", - Sequence: "Sequence", - MaybeModuleAccount: &cosmosapp.ModuleAccount{ - Name: "", - Permissions: []string{}, - }, - MaybeDelayedVestingAccount: &cosmosapp.DelayedVestingAccount{ - OriginalVesting: []cosmosapp.VestingBalance{}, - DelegatedFree: []cosmosapp.VestingBalance{}, - DelegatedVesting: []cosmosapp.VestingBalance{}, - EndTime: "", + mockAccountsView := account_view.NewMockAccountsView(nil).(*account_view.MockAccountsView) + mocks = append(mocks, &mockAccountsView.Mock) + + account.NewAccountsView = func(_ *rdb.Handle) account_view.Accounts { + return mockAccountsView + } + + mockAccountsView.On( + "IncrementUsableBalance", + "Address", + "Denom", + int64(10), + ).Return(nil) + + mockAccountsView.On( + "InsertAccountEvent", + account_view.AccountEvent{ + Address: "Address", + Type: "GenesisCreated", + Data: &event_usecase.GenesisCreated{ + Base: entity_event.Base{ + EventName: "GenesisCreated", + EventVersion: 1, + BlockHeight: 0, + EventUUID: "TESTUUID", + }, + Genesis: genesis.Genesis{ + GenesisTime: "", + ChainID: "", + InitialHeight: "", + ConsensusParams: genesis.ConsensusParams{}, + AppHash: "", + AppState: genesis.AppState{ + Auth: genesis.Auth{}, + Bank: genesis.Bank{ + Params: genesis.BankParams{}, + Balances: []genesis.Balance{ + { + Address: "Address", + Coins: []genesis.MinDeposit{ + { + Denom: "Denom", + Amount: strconv.Itoa(10), + }, + }, + }, + }, + Supply: nil, + DenomMetadata: nil, + }, + Capability: genesis.Capability{}, + Chainmain: genesis.Chainmain{}, + Distribution: genesis.Distribution{}, + Evidence: genesis.AppStateEvidence{}, + Genutil: genesis.Genutil{}, + Gov: genesis.Gov{}, + Ibc: genesis.Ibc{}, + Mint: genesis.Mint{}, + Params: nil, + Slashing: genesis.Slashing{}, + Staking: genesis.Staking{}, + Supply: genesis.Supply{}, + Transfer: genesis.Transfer{}, + Upgrade: genesis.Upgrade{}, + Vesting: genesis.Vesting{}, + }, + Validators: nil, + }, }, - MaybeContinuousVestingAccount: &cosmosapp.ContinuousVestingAccount{ - OriginalVesting: []cosmosapp.VestingBalance{}, - DelegatedFree: []cosmosapp.VestingBalance{}, - DelegatedVesting: []cosmosapp.VestingBalance{}, - StartTime: "", - EndTime: "", + }, + ).Return(nil) + + account.UpdateLastHandledEventHeight = func(_ *account.Account, _ *rdb.Handle, _ int64) error { + return nil + } + + return mocks + }, + }, + { + Name: "HandleCoinSpentEvent", + Events: []entity_event.Event{ + &event_usecase.CoinSpent{ + Base: entity_event.NewBase(entity_event.BaseParams{ + Name: event_usecase.COIN_SPENT, + Version: 1, + BlockHeight: 1, + }), + Address: "Address", + Amount: []coin.Coin{ + { + Denom: "Denom", + Amount: coin.NewInt(10), }, - MaybePeriodicVestingAccount: &cosmosapp.PeriodicVestingAccount{ - OriginalVesting: []cosmosapp.VestingBalance{}, - DelegatedFree: []cosmosapp.VestingBalance{}, - DelegatedVesting: []cosmosapp.VestingBalance{}, - StartTime: "", - EndTime: "", - VestingPeriods: []cosmosapp.VestingPeriod{}, + }, + }, + }, + MockFunc: func(mockClient *cosmosapp.MockClient) (mocks []*testify_mock.Mock) { + mockAccountsView := account_view.NewMockAccountsView(nil).(*account_view.MockAccountsView) + mocks = append(mocks, &mockAccountsView.Mock) + + account.NewAccountsView = func(_ *rdb.Handle) account_view.Accounts { + return mockAccountsView + } + + mockAccountsView.On( + "DecrementUsableBalance", + "Address", + "Denom", + int64(10), + ).Return(nil) + + mockAccountsView.On( + "InsertAccountEvent", + account_view.AccountEvent{ + Address: "Address", + Type: "CoinSpent", + Data: &event_usecase.CoinSpent{ + Base: entity_event.Base{ + EventName: "CoinSpent", + EventVersion: 1, + BlockHeight: 1, + EventUUID: "TESTUUID", + }, + Address: "Address", + Amount: []coin.Coin{ + { + Denom: "Denom", + Amount: coin.NewInt(10), + }, + }, }, + BlockHeight: 1, }, - nil, - ) + ).Return(nil) - mockClient.On("Balances", "Recipient").Return( - coin.Coins{ - coin.Coin{ + account.UpdateLastHandledEventHeight = func(_ *account.Account, _ *rdb.Handle, _ int64) error { + return nil + } + + return mocks + }, + }, + { + Name: "HandleCoinReceivedEvent", + Events: []entity_event.Event{ + &event_usecase.CoinReceived{ + Base: entity_event.NewBase(entity_event.BaseParams{ + Name: event_usecase.COIN_RECEIVED, + Version: 1, + BlockHeight: 1, + }), + Address: "Address", + Amount: []coin.Coin{ + { Denom: "Denom", - Amount: coin.NewInt(100), + Amount: coin.NewInt(10), }, }, - nil, - ) - + }, + }, + MockFunc: func(mockClient *cosmosapp.MockClient) (mocks []*testify_mock.Mock) { mockAccountsView := account_view.NewMockAccountsView(nil).(*account_view.MockAccountsView) mocks = append(mocks, &mockAccountsView.Mock) @@ -131,90 +286,276 @@ func TestAccount_HandleEvents(t *testing.T) { return mockAccountsView } - pubkey := "Key" + mockAccountsView.On( + "IncrementUsableBalance", + "Address", + "Denom", + int64(10), + ).Return(nil) mockAccountsView.On( - "Upsert", - &account_view.AccountRow{ - Address: "Recipient", - Type: "AccountType", - MaybeName: (*string)(nil), - MaybePubkey: &pubkey, - AccountNumber: "AccountNumber", - SequenceNumber: "Sequence", - Balance: coin.Coins{ - { - Denom: "Denom", - Amount: coin.NewInt(100), + "InsertAccountEvent", + account_view.AccountEvent{ + Address: "Address", + Type: "CoinReceived", + Data: &event_usecase.CoinReceived{ + Base: entity_event.Base{ + EventName: "CoinReceived", + EventVersion: 1, + BlockHeight: 1, + EventUUID: "TESTUUID", + }, + Address: "Address", + Amount: []coin.Coin{ + { + Denom: "Denom", + Amount: coin.NewInt(10), + }, }, }, + BlockHeight: 1, }, ).Return(nil) - mockClient.On("Account", "Sender").Return( - &cosmosapp.Account{ - Type: "AccountType", - Address: "Sender", - MaybePubkey: &cosmosapp.PubKey{ - Type: "PubKeyType", - Key: pubkey, - }, - AccountNumber: "AccountNumber", - Sequence: "Sequence", - MaybeModuleAccount: &cosmosapp.ModuleAccount{ - Name: "", - Permissions: []string{}, + account.UpdateLastHandledEventHeight = func(_ *account.Account, _ *rdb.Handle, _ int64) error { + return nil + } + + return mocks + }, + }, + { + Name: "HandleCoinMintEvent", + Events: []entity_event.Event{ + &event_usecase.CoinMint{ + Base: entity_event.NewBase(entity_event.BaseParams{ + Name: event_usecase.COIN_MINT, + Version: 1, + BlockHeight: 1, + }), + Address: "Address", + Amount: []coin.Coin{ + { + Denom: "Denom", + Amount: coin.NewInt(10), }, - MaybeDelayedVestingAccount: &cosmosapp.DelayedVestingAccount{ - OriginalVesting: []cosmosapp.VestingBalance{}, - DelegatedFree: []cosmosapp.VestingBalance{}, - DelegatedVesting: []cosmosapp.VestingBalance{}, - EndTime: "", + }, + }, + }, + MockFunc: func(mockClient *cosmosapp.MockClient) (mocks []*testify_mock.Mock) { + mockAccountsView := account_view.NewMockAccountsView(nil).(*account_view.MockAccountsView) + mocks = append(mocks, &mockAccountsView.Mock) + + account.NewAccountsView = func(_ *rdb.Handle) account_view.Accounts { + return mockAccountsView + } + + mockAccountsView.On( + "IncrementUsableBalance", + "Address", + "Denom", + int64(10), + ).Return(nil) + + mockAccountsView.On( + "InsertAccountEvent", + account_view.AccountEvent{ + Address: "Address", + Type: "CoinMint", + Data: &event_usecase.CoinMint{ + Base: entity_event.Base{ + EventName: "CoinMint", + EventVersion: 1, + BlockHeight: 1, + EventUUID: "TESTUUID", + }, + Address: "Address", + Amount: []coin.Coin{ + { + Denom: "Denom", + Amount: coin.NewInt(10), + }, + }, }, - MaybeContinuousVestingAccount: &cosmosapp.ContinuousVestingAccount{ - OriginalVesting: []cosmosapp.VestingBalance{}, - DelegatedFree: []cosmosapp.VestingBalance{}, - DelegatedVesting: []cosmosapp.VestingBalance{}, - StartTime: "", - EndTime: "", + BlockHeight: 1, + }, + ).Return(nil) + + account.UpdateLastHandledEventHeight = func(_ *account.Account, _ *rdb.Handle, _ int64) error { + return nil + } + + return mocks + }, + }, + { + Name: "HandleCoinBurnEvent", + Events: []entity_event.Event{ + &event_usecase.CoinBurn{ + Base: entity_event.NewBase(entity_event.BaseParams{ + Name: event_usecase.COIN_BURN, + Version: 1, + BlockHeight: 1, + }), + Address: "Address", + Amount: []coin.Coin{ + { + Denom: "Denom", + Amount: coin.NewInt(10), }, - MaybePeriodicVestingAccount: &cosmosapp.PeriodicVestingAccount{ - OriginalVesting: []cosmosapp.VestingBalance{}, - DelegatedFree: []cosmosapp.VestingBalance{}, - DelegatedVesting: []cosmosapp.VestingBalance{}, - StartTime: "", - EndTime: "", - VestingPeriods: []cosmosapp.VestingPeriod{}, + }, + }, + }, + MockFunc: func(mockClient *cosmosapp.MockClient) (mocks []*testify_mock.Mock) { + mockAccountsView := account_view.NewMockAccountsView(nil).(*account_view.MockAccountsView) + mocks = append(mocks, &mockAccountsView.Mock) + + account.NewAccountsView = func(_ *rdb.Handle) account_view.Accounts { + return mockAccountsView + } + + mockAccountsView.On( + "DecrementUsableBalance", + "Address", + "Denom", + int64(10), + ).Return(nil) + + mockAccountsView.On( + "InsertAccountEvent", + account_view.AccountEvent{ + Address: "Address", + Type: "CoinBurn", + Data: &event_usecase.CoinBurn{ + Base: entity_event.Base{ + EventName: "CoinBurn", + EventVersion: 1, + BlockHeight: 1, + EventUUID: "TESTUUID", + }, + Address: "Address", + Amount: []coin.Coin{ + { + Denom: "Denom", + Amount: coin.NewInt(10), + }, + }, }, + BlockHeight: 1, }, - nil, - ) + ).Return(nil) + + account.UpdateLastHandledEventHeight = func(_ *account.Account, _ *rdb.Handle, _ int64) error { + return nil + } - mockClient.On("Balances", "Sender").Return( - coin.Coins{ - coin.Coin{ + return mocks + }, + }, + { + Name: "HandleTransactionFailedEvent", + Events: []entity_event.Event{ + &event_usecase.TransactionFailed{ + Base: entity_event.NewBase(entity_event.BaseParams{ + Name: event_usecase.TRANSACTION_FAILED, + Version: 1, + BlockHeight: 1, + }), + TxHash: "", + Index: 0, + Code: 0, + Log: "", + MsgCount: 0, + Signers: []model.TransactionSigner{ + { + TransactionSignerInfo: model.TransactionSignerInfo{ + Type: "", + IsMultiSig: false, + Pubkeys: []string{ + "A5N3/S/r5nQjjvJpdXR5eY1QiebM9ULBsWVbCw/a6dA4", + }, + MaybeThreshold: nil, + AccountSequence: 0, + }, + Address: "prefix18mcwp6vtlvpgxy62eledk3chhjguw636muuqul", + }, + }, + Senders: nil, + Fee: []coin.Coin{ + { Denom: "Denom", - Amount: coin.NewInt(1000), + Amount: coin.NewInt(10), }, }, - nil, - ) + FeePayer: "", + FeeGranter: "", + GasWanted: 0, + GasUsed: 0, + Memo: "", + TimeoutHeight: 0, + }, + }, + MockFunc: func(mockClient *cosmosapp.MockClient) (mocks []*testify_mock.Mock) { + mockAccountsView := account_view.NewMockAccountsView(nil).(*account_view.MockAccountsView) + mocks = append(mocks, &mockAccountsView.Mock) + + account.NewAccountsView = func(_ *rdb.Handle) account_view.Accounts { + return mockAccountsView + } mockAccountsView.On( - "Upsert", - &account_view.AccountRow{ - Address: "Sender", - Type: "AccountType", - MaybeName: (*string)(nil), - MaybePubkey: &pubkey, - AccountNumber: "AccountNumber", - SequenceNumber: "Sequence", - Balance: coin.Coins{ - { - Denom: "Denom", - Amount: coin.NewInt(1000), + "DecrementUsableBalance", + "prefix18mcwp6vtlvpgxy62eledk3chhjguw636muuqul", + "Denom", + int64(10), + ).Return(nil) + + mockAccountsView.On( + "InsertAccountEvent", + account_view.AccountEvent{ + Address: "prefix18mcwp6vtlvpgxy62eledk3chhjguw636muuqul", + Type: "TransactionFailed", + Data: &event_usecase.TransactionFailed{ + Base: entity_event.Base{ + EventName: "TransactionFailed", + EventVersion: 1, + BlockHeight: 1, + EventUUID: "TESTUUID", + }, + TxHash: "", + Index: 0, + Code: 0, + Log: "", + MsgCount: 0, + Signers: []model.TransactionSigner{ + { + TransactionSignerInfo: model.TransactionSignerInfo{ + Type: "", + IsMultiSig: false, + Pubkeys: []string{ + "A5N3/S/r5nQjjvJpdXR5eY1QiebM9ULBsWVbCw/a6dA4", + }, + MaybeThreshold: nil, + AccountSequence: 0, + }, + Address: "prefix18mcwp6vtlvpgxy62eledk3chhjguw636muuqul", + }, }, + Senders: nil, + Fee: []coin.Coin{ + { + Denom: "Denom", + Amount: coin.NewInt(10), + }, + }, + FeePayer: "", + FeeGranter: "", + GasWanted: 0, + GasUsed: 0, + Memo: "", + TimeoutHeight: 0, }, + BlockHeight: 1, }, ).Return(nil) diff --git a/projection/account/migrations/20211130143810_usable_balance.down.sql b/projection/account/migrations/20211130143810_usable_balance.down.sql new file mode 100644 index 00000000..a485db06 --- /dev/null +++ b/projection/account/migrations/20211130143810_usable_balance.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE view_accounts + DROP usable_balance; diff --git a/projection/account/migrations/20211130143810_usable_balance.up.sql b/projection/account/migrations/20211130143810_usable_balance.up.sql new file mode 100644 index 00000000..7a04c7f9 --- /dev/null +++ b/projection/account/migrations/20211130143810_usable_balance.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE view_accounts + ADD usable_balance JSONB NOT NULL DEFAULT '{}'; diff --git a/projection/account/migrations/20220112140353_drop_unnecessary_col.down.sql b/projection/account/migrations/20220112140353_drop_unnecessary_col.down.sql new file mode 100644 index 00000000..5f768938 --- /dev/null +++ b/projection/account/migrations/20220112140353_drop_unnecessary_col.down.sql @@ -0,0 +1,8 @@ +ALTER TABLE view_accounts + ADD name VARCHAR NULL; +ALTER TABLE view_accounts + ADD pubkey VARCHAR NULL; +ALTER TABLE view_accounts + ADD account_type VARCHAR; +ALTER TABLE view_accounts + ADD balance JSONB; diff --git a/projection/account/migrations/20220112140353_drop_unnecessary_col.up.sql b/projection/account/migrations/20220112140353_drop_unnecessary_col.up.sql new file mode 100644 index 00000000..75b3b13c --- /dev/null +++ b/projection/account/migrations/20220112140353_drop_unnecessary_col.up.sql @@ -0,0 +1,11 @@ +ALTER TABLE view_accounts + DROP name; + +ALTER TABLE view_accounts + DROP pubkey; + +ALTER TABLE view_accounts + DROP account_type; + +ALTER TABLE view_accounts + DROP balance; \ No newline at end of file diff --git a/projection/account/migrations/20220113121738_view_account_events.down.sql b/projection/account/migrations/20220113121738_view_account_events.down.sql new file mode 100644 index 00000000..04831141 --- /dev/null +++ b/projection/account/migrations/20220113121738_view_account_events.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS view_account_events; diff --git a/projection/account/migrations/20220113121738_view_account_events.up.sql b/projection/account/migrations/20220113121738_view_account_events.up.sql new file mode 100644 index 00000000..b8bb5f69 --- /dev/null +++ b/projection/account/migrations/20220113121738_view_account_events.up.sql @@ -0,0 +1,6 @@ +CREATE TABLE view_account_events ( + address VARCHAR, + block_height BIGINT, + type VARCHAR, + data JSONB +); \ No newline at end of file diff --git a/projection/account/view/accounts.go b/projection/account/view/accounts.go index 5ba45997..79966715 100644 --- a/projection/account/view/accounts.go +++ b/projection/account/view/accounts.go @@ -4,10 +4,9 @@ import ( "errors" "fmt" - "github.com/crypto-com/chain-indexing/external/json" - "github.com/crypto-com/chain-indexing/usecase/coin" - "github.com/crypto-com/chain-indexing/appinterface/projection/view" + "github.com/crypto-com/chain-indexing/external/json" + jsoniter "github.com/json-iterator/go" "github.com/crypto-com/chain-indexing/appinterface/pagination" "github.com/crypto-com/chain-indexing/appinterface/rdb" @@ -15,9 +14,11 @@ import ( ) type Accounts interface { - Upsert(*AccountRow) error FindBy(*AccountIdentity) (*AccountRow, error) List(AccountsListOrder, *pagination.Pagination) ([]AccountRow, *pagination.PaginationResult, error) + IncrementUsableBalance(string, string, int64) error + DecrementUsableBalance(string, string, int64) error + InsertAccountEvent(AccountEvent) error } type AccountsView struct { @@ -38,34 +39,113 @@ func NewAccountsView(handle *rdb.Handle) Accounts { } } -func (accountsView *AccountsView) Upsert(account *AccountRow) error { +func (accountsView *AccountsView) InsertAccountEvent(accountEvent AccountEvent) error { + var accountEventDataJSON string + var err error + if accountEventDataJSON, err = jsoniter.MarshalToString(accountEvent.Data); err != nil { + return fmt.Errorf("error JSON marshalling block event data for insertion: %v: %w", err, rdb.ErrBuildSQLStmt) + } + + sql, sqlArgs, err := accountsView.rdb.StmtBuilder. + Insert( + "view_account_events", + ). + Columns( + "address", + "data", + "type", + "block_height", + ). + Values( + accountEvent.Address, + accountEventDataJSON, + accountEvent.Type, + accountEvent.BlockHeight, + ). + ToSql() + + if err != nil { + return fmt.Errorf("error building account events insert sql: %v: %w", err, rdb.ErrBuildSQLStmt) + } + + result, err := accountsView.rdb.Exec(sql, sqlArgs...) + if err != nil { + return fmt.Errorf("error inserting account events into the table: %v: %w", err, rdb.ErrWrite) + } + if result.RowsAffected() != 1 { + return fmt.Errorf("error inserting account events into the table: no rows updated: %w", rdb.ErrWrite) + } + + return nil +} + +func (accountsView *AccountsView) IncrementUsableBalance(address string, denom string, increment int64) error { + initUsableBalance := make(map[string]int64) + initUsableBalance[denom] = increment + + var initUsableBalanceJSON string + var err error + if initUsableBalanceJSON, err = jsoniter.MarshalToString(initUsableBalance); err != nil { + return fmt.Errorf("error JSON marshalling block event data for insertion: %v: %w", err, rdb.ErrBuildSQLStmt) + } + + sql, sqlArgs, err := accountsView.rdb.StmtBuilder. + Insert( + "view_accounts", + ). + Columns( + "address", + "usable_balance", + ). + Values( + address, + initUsableBalanceJSON, + ). + Suffix(fmt.Sprintf("ON CONFLICT(address) DO UPDATE SET usable_balance = jsonb_set(view_accounts.usable_balance, '{%s}', (COALESCE(view_accounts.usable_balance->>'%s','0')::bigint + %d)::text::jsonb)", denom, denom, increment)). + ToSql() + + if err != nil { + return fmt.Errorf("error building accounts update sql: %v: %w", err, rdb.ErrBuildSQLStmt) + } + + result, err := accountsView.rdb.Exec(sql, sqlArgs...) + if err != nil { + return fmt.Errorf("error updating account into the table: %v: %w", err, rdb.ErrWrite) + } + if result.RowsAffected() != 1 { + return fmt.Errorf("error updating account into the table: no rows updated: %w", rdb.ErrWrite) + } + + return nil +} + +func (accountsView *AccountsView) DecrementUsableBalance(address string, denom string, decrement int64) error { + initUsableBalance := make(map[string]int64) + initUsableBalance[denom] = -decrement + + var initUsableBalanceJSON string + var err error + if initUsableBalanceJSON, err = jsoniter.MarshalToString(initUsableBalance); err != nil { + return fmt.Errorf("error JSON marshalling block event data for insertion: %v: %w", err, rdb.ErrBuildSQLStmt) + } + sql, sqlArgs, err := accountsView.rdb.StmtBuilder. Insert( "view_accounts", ). Columns( "address", - "account_type", - "name", - "pubkey", - "account_number", - "sequence_number", - "balance", + "usable_balance", ). Values( - account.Address, - account.Type, - account.MaybeName, - account.MaybePubkey, - account.AccountNumber, - account.SequenceNumber, - json.MustMarshalToString(account.Balance), + address, + initUsableBalanceJSON, ). - Suffix("ON CONFLICT(address) DO UPDATE SET balance = EXCLUDED.balance"). + Suffix(fmt.Sprintf("ON CONFLICT(address) DO UPDATE SET usable_balance = jsonb_set(view_accounts.usable_balance, '{%s}', (COALESCE(view_accounts.usable_balance->>'%s','0')::bigint - %d)::text::jsonb)", denom, denom, decrement)). ToSql() if err != nil { - return fmt.Errorf("error building accounts insertion sql: %v: %w", err, rdb.ErrBuildSQLStmt) + return fmt.Errorf("error building accounts insert sql: %v: %w", err, rdb.ErrBuildSQLStmt) } result, err := accountsView.rdb.Exec(sql, sqlArgs...) @@ -73,7 +153,7 @@ func (accountsView *AccountsView) Upsert(account *AccountRow) error { return fmt.Errorf("error inserting account into the table: %v: %w", err, rdb.ErrWrite) } if result.RowsAffected() != 1 { - return fmt.Errorf("error inserting account into the table: no rows inserted: %w", rdb.ErrWrite) + return fmt.Errorf("error updating account into the table: no rows updated: %w", rdb.ErrWrite) } return nil @@ -84,13 +164,9 @@ func (accountsView *AccountsView) FindBy(identity *AccountIdentity) (*AccountRow selectStmtBuilder := accountsView.rdb.StmtBuilder.Select( "address", - "account_type", - "name", - "pubkey", "account_number", "sequence_number", - - "account_balance", "account_denom", + "usable_balance", ).From("view_accounts") selectStmtBuilder = selectStmtBuilder.Where("address = ?", identity.Address) @@ -101,15 +177,12 @@ func (accountsView *AccountsView) FindBy(identity *AccountIdentity) (*AccountRow } var account AccountRow - var balance string + var usableBalance string if err = accountsView.rdb.QueryRow(sql, sqlArgs...).Scan( &account.Address, - &account.Type, - &account.MaybeName, - &account.MaybePubkey, &account.AccountNumber, &account.SequenceNumber, - &balance, + &usableBalance, ); err != nil { if errors.Is(err, rdb.ErrNoRows) { return nil, rdb.ErrNoRows @@ -117,7 +190,7 @@ func (accountsView *AccountsView) FindBy(identity *AccountIdentity) (*AccountRow return nil, fmt.Errorf("error scanning account row: %v: %w", err, rdb.ErrQuery) } - json.MustUnmarshalFromString(balance, &account.Balance) + json.MustUnmarshalFromString(usableBalance, &account.UsableBalance) return &account, nil } @@ -127,12 +200,9 @@ func (accountsView *AccountsView) List( ) ([]AccountRow, *pagination.PaginationResult, error) { stmtBuilder := accountsView.rdb.StmtBuilder.Select( "address", - "account_type", - "name", - "pubkey", "account_number", "sequence_number", - "balance", + "usable_balance", ).From( "view_accounts", ) @@ -161,15 +231,12 @@ func (accountsView *AccountsView) List( accounts := make([]AccountRow, 0) for rowsResult.Next() { var account AccountRow - var balance string + var usableBalance string if err = rowsResult.Scan( &account.Address, - &account.Type, - &account.MaybeName, - &account.MaybePubkey, &account.AccountNumber, &account.SequenceNumber, - &balance, + &usableBalance, ); err != nil { if errors.Is(err, rdb.ErrNoRows) { return nil, nil, rdb.ErrNoRows @@ -177,7 +244,7 @@ func (accountsView *AccountsView) List( return nil, nil, fmt.Errorf("error scanning account row: %v: %w", err, rdb.ErrQuery) } - json.MustUnmarshalFromString(balance, &account.Balance) + json.MustUnmarshalFromString(usableBalance, &account.UsableBalance) accounts = append(accounts, account) } @@ -190,11 +257,19 @@ func (accountsView *AccountsView) List( } type AccountRow struct { - Address string `json:"address"` - Type string `json:"type"` - MaybeName *string `json:"name"` - MaybePubkey *string `json:"pubkey"` - AccountNumber string `json:"accountNumber"` - SequenceNumber string `json:"sequenceNumber"` - Balance coin.Coins `json:"balance"` + Address string `json:"address"` + AccountNumber string `json:"accountNumber"` + SequenceNumber string `json:"sequenceNumber"` + UsableBalance AccountBalance `json:"usableBalance"` +} + +type AccountBalanceDenom string + +type AccountBalance map[AccountBalanceDenom]int64 + +type AccountEvent struct { + Address string `json:"address"` + Type string `json:"type"` + Data interface{} `json:"data"` + BlockHeight int64 `json:"block_height"` } diff --git a/projection/account/view/accounts_mock.go b/projection/account/view/accounts_mock.go index ad81ebfe..9b1644e9 100644 --- a/projection/account/view/accounts_mock.go +++ b/projection/account/view/accounts_mock.go @@ -3,6 +3,7 @@ package view import ( "github.com/crypto-com/chain-indexing/appinterface/pagination" "github.com/crypto-com/chain-indexing/appinterface/rdb" + event_usecase "github.com/crypto-com/chain-indexing/usecase/event" "github.com/stretchr/testify/mock" ) @@ -10,16 +11,10 @@ type MockAccountsView struct { mock.Mock } - func NewMockAccountsView(_ *rdb.Handle) Accounts { return &MockAccountsView{} } -func (accountsView *MockAccountsView) Upsert(account *AccountRow) error { - mockArgs := accountsView.Called(account) - return mockArgs.Error(0) -} - func (accountsView *MockAccountsView) FindBy(identity *AccountIdentity) (*AccountRow, error) { mockArgs := accountsView.Called(identity) result, _ := mockArgs.Get(0).(*AccountRow) @@ -35,3 +30,31 @@ func (accountsView *MockAccountsView) List( result1, _ := mockArgs.Get(1).(*pagination.PaginationResult) return result0, result1, mockArgs.Error(2) } + +func (accountsView *MockAccountsView) IncrementUsableBalance(address string, denom string, increment int64) error { + mockArgs := accountsView.Called(address, denom, increment) + return mockArgs.Error(0) +} + +func (accountsView *MockAccountsView) DecrementUsableBalance(address string, denom string, decrement int64) error { + mockArgs := accountsView.Called(address, denom, decrement) + return mockArgs.Error(0) +} + +func (accountsView *MockAccountsView) InsertAccountEvent(event AccountEvent) error { + if data, ok := event.Data.(*event_usecase.GenesisCreated); ok { + data.EventUUID = "TESTUUID" + } else if data, ok := event.Data.(*event_usecase.CoinSpent); ok { + data.EventUUID = "TESTUUID" + } else if data, ok := event.Data.(*event_usecase.CoinReceived); ok { + data.EventUUID = "TESTUUID" + } else if data, ok := event.Data.(*event_usecase.CoinMint); ok { + data.EventUUID = "TESTUUID" + } else if data, ok := event.Data.(*event_usecase.CoinBurn); ok { + data.EventUUID = "TESTUUID" + } else if data, ok := event.Data.(*event_usecase.TransactionFailed); ok { + data.EventUUID = "TESTUUID" + } + mockArgs := accountsView.Called(event) + return mockArgs.Error(0) +} diff --git a/usecase/command/create_coin_burn.go b/usecase/command/create_coin_burn.go new file mode 100644 index 00000000..74764957 --- /dev/null +++ b/usecase/command/create_coin_burn.go @@ -0,0 +1,38 @@ +package command + +import ( + entity_event "github.com/crypto-com/chain-indexing/entity/event" + "github.com/crypto-com/chain-indexing/usecase/event" + "github.com/crypto-com/chain-indexing/usecase/model" +) + +type CreateCoinBurn struct { + blockHeight int64 + params model.CoinBurnParams +} + +func NewCreateCoinBurn( + blockHeight int64, + params model.CoinBurnParams, +) *CreateCoinBurn { + return &CreateCoinBurn{ + blockHeight, + params, + } +} + +// Name returns name of command +func (*CreateCoinBurn) Name() string { + return "CreateCoinBurn" +} + +// Version returns version of command +func (*CreateCoinBurn) Version() int { + return 1 +} + +// Exec process the command data and return the event accordingly +func (cmd *CreateCoinBurn) Exec() (entity_event.Event, error) { + event := event.NewCoinBurn(cmd.blockHeight, cmd.params) + return event, nil +} diff --git a/usecase/command/create_coin_mint.go b/usecase/command/create_coin_mint.go new file mode 100644 index 00000000..ca978b77 --- /dev/null +++ b/usecase/command/create_coin_mint.go @@ -0,0 +1,38 @@ +package command + +import ( + entity_event "github.com/crypto-com/chain-indexing/entity/event" + "github.com/crypto-com/chain-indexing/usecase/event" + "github.com/crypto-com/chain-indexing/usecase/model" +) + +type CreateCoinMint struct { + blockHeight int64 + params model.CoinMintParams +} + +func NewCreateCoinMint( + blockHeight int64, + params model.CoinMintParams, +) *CreateCoinMint { + return &CreateCoinMint{ + blockHeight, + params, + } +} + +// Name returns name of command +func (*CreateCoinMint) Name() string { + return "CreateCoinMint" +} + +// Version returns version of command +func (*CreateCoinMint) Version() int { + return 1 +} + +// Exec process the command data and return the event accordingly +func (cmd *CreateCoinMint) Exec() (entity_event.Event, error) { + event := event.NewCoinMint(cmd.blockHeight, cmd.params) + return event, nil +} diff --git a/usecase/command/create_coin_received.go b/usecase/command/create_coin_received.go new file mode 100644 index 00000000..581f51b3 --- /dev/null +++ b/usecase/command/create_coin_received.go @@ -0,0 +1,38 @@ +package command + +import ( + entity_event "github.com/crypto-com/chain-indexing/entity/event" + "github.com/crypto-com/chain-indexing/usecase/event" + "github.com/crypto-com/chain-indexing/usecase/model" +) + +type CreateCoinReceived struct { + blockHeight int64 + params model.CoinReceivedParams +} + +func NewCreateCoinReceived( + blockHeight int64, + params model.CoinReceivedParams, +) *CreateCoinReceived { + return &CreateCoinReceived{ + blockHeight, + params, + } +} + +// Name returns name of command +func (*CreateCoinReceived) Name() string { + return "CreateCoinReceived" +} + +// Version returns version of command +func (*CreateCoinReceived) Version() int { + return 1 +} + +// Exec process the command data and return the event accordingly +func (cmd *CreateCoinReceived) Exec() (entity_event.Event, error) { + event := event.NewCoinReceived(cmd.blockHeight, cmd.params) + return event, nil +} diff --git a/usecase/command/create_coin_spent.go b/usecase/command/create_coin_spent.go new file mode 100644 index 00000000..dc0d72e9 --- /dev/null +++ b/usecase/command/create_coin_spent.go @@ -0,0 +1,38 @@ +package command + +import ( + entity_event "github.com/crypto-com/chain-indexing/entity/event" + "github.com/crypto-com/chain-indexing/usecase/event" + "github.com/crypto-com/chain-indexing/usecase/model" +) + +type CreateCoinSpent struct { + blockHeight int64 + params model.CoinSpentParams +} + +func NewCreateCoinSpent( + blockHeight int64, + params model.CoinSpentParams, +) *CreateCoinSpent { + return &CreateCoinSpent{ + blockHeight, + params, + } +} + +// Name returns name of command +func (*CreateCoinSpent) Name() string { + return "CreateCoinSpent" +} + +// Version returns version of command +func (*CreateCoinSpent) Version() int { + return 1 +} + +// Exec process the command data and return the event accordingly +func (cmd *CreateCoinSpent) Exec() (entity_event.Event, error) { + event := event.NewCoinSpent(cmd.blockHeight, cmd.params) + return event, nil +} diff --git a/usecase/event/coin_burn.go b/usecase/event/coin_burn.go new file mode 100644 index 00000000..8870a551 --- /dev/null +++ b/usecase/event/coin_burn.go @@ -0,0 +1,59 @@ +package event + +import ( + "bytes" + + jsoniter "github.com/json-iterator/go" + + event_entity "github.com/crypto-com/chain-indexing/entity/event" + "github.com/crypto-com/chain-indexing/usecase/coin" + "github.com/crypto-com/chain-indexing/usecase/model" + "github.com/luci/go-render/render" +) + +const COIN_BURN = "CoinBurn" + +type CoinBurn struct { + event_entity.Base + + Address string `json:"address"` + Amount coin.Coins `json:"amount"` +} + +func NewCoinBurn(blockHeight int64, params model.CoinBurnParams) *CoinBurn { + return &CoinBurn{ + event_entity.NewBase(event_entity.BaseParams{ + Name: COIN_BURN, + Version: 1, + BlockHeight: blockHeight, + }), + + params.Address, + params.Amount, + } + +} +func (event *CoinBurn) ToJSON() (string, error) { + encoded, err := jsoniter.Marshal(event) + if err != nil { + return "", err + } + + return string(encoded), nil +} + +func (event *CoinBurn) String() string { + return render.Render(event) +} + +func DecodeCoinBurn(encoded []byte) (event_entity.Event, error) { + jsonDecoder := jsoniter.NewDecoder(bytes.NewReader(encoded)) + jsonDecoder.DisallowUnknownFields() + + var event *CoinBurn + if err := jsonDecoder.Decode(&event); err != nil { + return nil, err + } + + return event, nil +} diff --git a/usecase/event/coin_burn_test.go b/usecase/event/coin_burn_test.go new file mode 100644 index 00000000..7aa14d4a --- /dev/null +++ b/usecase/event/coin_burn_test.go @@ -0,0 +1,43 @@ +package event_test + +import ( + event_entity "github.com/crypto-com/chain-indexing/entity/event" + "github.com/crypto-com/chain-indexing/usecase/coin" + "github.com/crypto-com/chain-indexing/usecase/model" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + event_usecase "github.com/crypto-com/chain-indexing/usecase/event" +) + +var _ = Describe("Event", func() { + registry := event_entity.NewRegistry() + event_usecase.RegisterEvents(registry) + + Describe("En/DecodeCoinBurn", func() { + It("should able to encode and decode to the same event", func() { + anyHeight := int64(1000) + anyAddress := "tcrocncl1sruzd529lhjju6hfcwd2fxp3v0e7p0vqqtme76" + anyAmount := coin.MustParseCoinsNormalized("123456basetcro,456789tcro") + event := event_usecase.NewCoinBurn(anyHeight, model.CoinBurnParams{ + Address: anyAddress, + Amount: anyAmount, + }) + + encoded, err := event.ToJSON() + Expect(err).To(BeNil()) + + decodedEvent, err := registry.DecodeByType( + event_usecase.COIN_BURN, 1, []byte(encoded), + ) + Expect(err).To(BeNil()) + Expect(decodedEvent).To(Equal(event)) + typedEvent, _ := decodedEvent.(*event_usecase.CoinBurn) + Expect(typedEvent.Name()).To(Equal(event_usecase.COIN_BURN)) + Expect(typedEvent.Version()).To(Equal(1)) + + Expect(typedEvent.Address).To(Equal(anyAddress)) + Expect(typedEvent.Amount).To(Equal(anyAmount)) + }) + }) +}) diff --git a/usecase/event/coin_mint.go b/usecase/event/coin_mint.go new file mode 100644 index 00000000..e638085f --- /dev/null +++ b/usecase/event/coin_mint.go @@ -0,0 +1,59 @@ +package event + +import ( + "bytes" + + jsoniter "github.com/json-iterator/go" + + event_entity "github.com/crypto-com/chain-indexing/entity/event" + "github.com/crypto-com/chain-indexing/usecase/coin" + "github.com/crypto-com/chain-indexing/usecase/model" + "github.com/luci/go-render/render" +) + +const COIN_MINT = "CoinMint" + +type CoinMint struct { + event_entity.Base + + Address string `json:"address"` + Amount coin.Coins `json:"amount"` +} + +func NewCoinMint(blockHeight int64, params model.CoinMintParams) *CoinMint { + return &CoinMint{ + event_entity.NewBase(event_entity.BaseParams{ + Name: COIN_MINT, + Version: 1, + BlockHeight: blockHeight, + }), + + params.Address, + params.Amount, + } + +} +func (event *CoinMint) ToJSON() (string, error) { + encoded, err := jsoniter.Marshal(event) + if err != nil { + return "", err + } + + return string(encoded), nil +} + +func (event *CoinMint) String() string { + return render.Render(event) +} + +func DecodeCoinMint(encoded []byte) (event_entity.Event, error) { + jsonDecoder := jsoniter.NewDecoder(bytes.NewReader(encoded)) + jsonDecoder.DisallowUnknownFields() + + var event *CoinMint + if err := jsonDecoder.Decode(&event); err != nil { + return nil, err + } + + return event, nil +} diff --git a/usecase/event/coin_mint_test.go b/usecase/event/coin_mint_test.go new file mode 100644 index 00000000..c15bd3c6 --- /dev/null +++ b/usecase/event/coin_mint_test.go @@ -0,0 +1,43 @@ +package event_test + +import ( + event_entity "github.com/crypto-com/chain-indexing/entity/event" + "github.com/crypto-com/chain-indexing/usecase/coin" + "github.com/crypto-com/chain-indexing/usecase/model" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + event_usecase "github.com/crypto-com/chain-indexing/usecase/event" +) + +var _ = Describe("Event", func() { + registry := event_entity.NewRegistry() + event_usecase.RegisterEvents(registry) + + Describe("En/DecodeCoinMint", func() { + It("should able to encode and decode to the same event", func() { + anyHeight := int64(1000) + anyAddress := "tcrocncl1sruzd529lhjju6hfcwd2fxp3v0e7p0vqqtme76" + anyAmount := coin.MustParseCoinsNormalized("123456basetcro,456789tcro") + event := event_usecase.NewCoinMint(anyHeight, model.CoinMintParams{ + Address: anyAddress, + Amount: anyAmount, + }) + + encoded, err := event.ToJSON() + Expect(err).To(BeNil()) + + decodedEvent, err := registry.DecodeByType( + event_usecase.COIN_MINT, 1, []byte(encoded), + ) + Expect(err).To(BeNil()) + Expect(decodedEvent).To(Equal(event)) + typedEvent, _ := decodedEvent.(*event_usecase.CoinMint) + Expect(typedEvent.Name()).To(Equal(event_usecase.COIN_MINT)) + Expect(typedEvent.Version()).To(Equal(1)) + + Expect(typedEvent.Address).To(Equal(anyAddress)) + Expect(typedEvent.Amount).To(Equal(anyAmount)) + }) + }) +}) diff --git a/usecase/event/coin_received.go b/usecase/event/coin_received.go new file mode 100644 index 00000000..23d04b93 --- /dev/null +++ b/usecase/event/coin_received.go @@ -0,0 +1,59 @@ +package event + +import ( + "bytes" + + jsoniter "github.com/json-iterator/go" + + event_entity "github.com/crypto-com/chain-indexing/entity/event" + "github.com/crypto-com/chain-indexing/usecase/coin" + "github.com/crypto-com/chain-indexing/usecase/model" + "github.com/luci/go-render/render" +) + +const COIN_RECEIVED = "CoinReceived" + +type CoinReceived struct { + event_entity.Base + + Address string `json:"address"` + Amount coin.Coins `json:"amount"` +} + +func NewCoinReceived(blockHeight int64, params model.CoinReceivedParams) *CoinReceived { + return &CoinReceived{ + event_entity.NewBase(event_entity.BaseParams{ + Name: COIN_RECEIVED, + Version: 1, + BlockHeight: blockHeight, + }), + + params.Address, + params.Amount, + } + +} +func (event *CoinReceived) ToJSON() (string, error) { + encoded, err := jsoniter.Marshal(event) + if err != nil { + return "", err + } + + return string(encoded), nil +} + +func (event *CoinReceived) String() string { + return render.Render(event) +} + +func DecodeCoinReceived(encoded []byte) (event_entity.Event, error) { + jsonDecoder := jsoniter.NewDecoder(bytes.NewReader(encoded)) + jsonDecoder.DisallowUnknownFields() + + var event *CoinReceived + if err := jsonDecoder.Decode(&event); err != nil { + return nil, err + } + + return event, nil +} diff --git a/usecase/event/coin_received_test.go b/usecase/event/coin_received_test.go new file mode 100644 index 00000000..aaa63478 --- /dev/null +++ b/usecase/event/coin_received_test.go @@ -0,0 +1,43 @@ +package event_test + +import ( + event_entity "github.com/crypto-com/chain-indexing/entity/event" + "github.com/crypto-com/chain-indexing/usecase/coin" + "github.com/crypto-com/chain-indexing/usecase/model" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + event_usecase "github.com/crypto-com/chain-indexing/usecase/event" +) + +var _ = Describe("Event", func() { + registry := event_entity.NewRegistry() + event_usecase.RegisterEvents(registry) + + Describe("En/DecodeCoinReceived", func() { + It("should able to encode and decode to the same event", func() { + anyHeight := int64(1000) + anyAddress := "tcrocncl1sruzd529lhjju6hfcwd2fxp3v0e7p0vqqtme76" + anyAmount := coin.MustParseCoinsNormalized("123456basetcro,456789tcro") + event := event_usecase.NewCoinReceived(anyHeight, model.CoinReceivedParams{ + Address: anyAddress, + Amount: anyAmount, + }) + + encoded, err := event.ToJSON() + Expect(err).To(BeNil()) + + decodedEvent, err := registry.DecodeByType( + event_usecase.COIN_RECEIVED, 1, []byte(encoded), + ) + Expect(err).To(BeNil()) + Expect(decodedEvent).To(Equal(event)) + typedEvent, _ := decodedEvent.(*event_usecase.CoinReceived) + Expect(typedEvent.Name()).To(Equal(event_usecase.COIN_RECEIVED)) + Expect(typedEvent.Version()).To(Equal(1)) + + Expect(typedEvent.Address).To(Equal(anyAddress)) + Expect(typedEvent.Amount).To(Equal(anyAmount)) + }) + }) +}) diff --git a/usecase/event/coin_spent.go b/usecase/event/coin_spent.go new file mode 100644 index 00000000..790a806d --- /dev/null +++ b/usecase/event/coin_spent.go @@ -0,0 +1,59 @@ +package event + +import ( + "bytes" + + jsoniter "github.com/json-iterator/go" + + event_entity "github.com/crypto-com/chain-indexing/entity/event" + "github.com/crypto-com/chain-indexing/usecase/coin" + "github.com/crypto-com/chain-indexing/usecase/model" + "github.com/luci/go-render/render" +) + +const COIN_SPENT = "CoinSpent" + +type CoinSpent struct { + event_entity.Base + + Address string `json:"address"` + Amount coin.Coins `json:"amount"` +} + +func NewCoinSpent(blockHeight int64, params model.CoinSpentParams) *CoinSpent { + return &CoinSpent{ + event_entity.NewBase(event_entity.BaseParams{ + Name: COIN_SPENT, + Version: 1, + BlockHeight: blockHeight, + }), + + params.Address, + params.Amount, + } + +} +func (event *CoinSpent) ToJSON() (string, error) { + encoded, err := jsoniter.Marshal(event) + if err != nil { + return "", err + } + + return string(encoded), nil +} + +func (event *CoinSpent) String() string { + return render.Render(event) +} + +func DecodeCoinSpent(encoded []byte) (event_entity.Event, error) { + jsonDecoder := jsoniter.NewDecoder(bytes.NewReader(encoded)) + jsonDecoder.DisallowUnknownFields() + + var event *CoinSpent + if err := jsonDecoder.Decode(&event); err != nil { + return nil, err + } + + return event, nil +} diff --git a/usecase/event/coin_spent_test.go b/usecase/event/coin_spent_test.go new file mode 100644 index 00000000..b3f14904 --- /dev/null +++ b/usecase/event/coin_spent_test.go @@ -0,0 +1,43 @@ +package event_test + +import ( + event_entity "github.com/crypto-com/chain-indexing/entity/event" + "github.com/crypto-com/chain-indexing/usecase/coin" + "github.com/crypto-com/chain-indexing/usecase/model" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + event_usecase "github.com/crypto-com/chain-indexing/usecase/event" +) + +var _ = Describe("Event", func() { + registry := event_entity.NewRegistry() + event_usecase.RegisterEvents(registry) + + Describe("En/DecodeCoinSpent", func() { + It("should able to encode and decode to the same event", func() { + anyHeight := int64(1000) + anyAddress := "tcrocncl1sruzd529lhjju6hfcwd2fxp3v0e7p0vqqtme76" + anyAmount := coin.MustParseCoinsNormalized("123456basetcro,456789tcro") + event := event_usecase.NewCoinSpent(anyHeight, model.CoinSpentParams{ + Address: anyAddress, + Amount: anyAmount, + }) + + encoded, err := event.ToJSON() + Expect(err).To(BeNil()) + + decodedEvent, err := registry.DecodeByType( + event_usecase.COIN_SPENT, 1, []byte(encoded), + ) + Expect(err).To(BeNil()) + Expect(decodedEvent).To(Equal(event)) + typedEvent, _ := decodedEvent.(*event_usecase.CoinSpent) + Expect(typedEvent.Name()).To(Equal(event_usecase.COIN_SPENT)) + Expect(typedEvent.Version()).To(Equal(1)) + + Expect(typedEvent.Address).To(Equal(anyAddress)) + Expect(typedEvent.Amount).To(Equal(anyAmount)) + }) + }) +}) diff --git a/usecase/event/events.go b/usecase/event/events.go index 18c93829..b0d9d060 100644 --- a/usecase/event/events.go +++ b/usecase/event/events.go @@ -27,6 +27,10 @@ func RegisterEvents(registry *event.Registry) { registry.Register(MSG_SEND_FAILED, 1, DecodeMsgSend) registry.Register(MSG_MULTI_SEND_CREATED, 1, DecodeMsgMultiSend) registry.Register(MSG_MULTI_SEND_FAILED, 1, DecodeMsgMultiSend) + registry.Register(COIN_SPENT, 1, DecodeCoinSpent) + registry.Register(COIN_RECEIVED, 1, DecodeCoinReceived) + registry.Register(COIN_BURN, 1, DecodeCoinBurn) + registry.Register(COIN_MINT, 1, DecodeCoinMint) // Distribution registry.Register(MSG_SET_WITHDRAW_ADDRESS_CREATED, 1, DecodeMsgSetWithdrawAddress) diff --git a/usecase/model/blockresults.go b/usecase/model/blockresults.go index da5c4c40..1ec77b3a 100644 --- a/usecase/model/blockresults.go +++ b/usecase/model/blockresults.go @@ -9,6 +9,7 @@ import ( type BlockResults struct { Height int64 `json:"height"` TxsResults []BlockResultsTxsResult `json:"txsResults"` + BlockEvents []BlockResultsEvent `json:"blockEvents"` BeginBlockEvents []BlockResultsEvent `json:"beginBlockEvents"` EndBlockEvents []BlockResultsEvent `json:"endBlockEvents"` ValidatorUpdates []BlockResultsValidatorUpdate `json:"validatorUpdates"` diff --git a/usecase/model/coin_burn.go b/usecase/model/coin_burn.go new file mode 100644 index 00000000..990a6005 --- /dev/null +++ b/usecase/model/coin_burn.go @@ -0,0 +1,8 @@ +package model + +import "github.com/crypto-com/chain-indexing/usecase/coin" + +type CoinBurnParams struct { + Address string + Amount coin.Coins +} diff --git a/usecase/model/coin_mint.go b/usecase/model/coin_mint.go new file mode 100644 index 00000000..b9d3db85 --- /dev/null +++ b/usecase/model/coin_mint.go @@ -0,0 +1,8 @@ +package model + +import "github.com/crypto-com/chain-indexing/usecase/coin" + +type CoinMintParams struct { + Address string + Amount coin.Coins +} diff --git a/usecase/model/coin_received.go b/usecase/model/coin_received.go new file mode 100644 index 00000000..3db18f33 --- /dev/null +++ b/usecase/model/coin_received.go @@ -0,0 +1,8 @@ +package model + +import "github.com/crypto-com/chain-indexing/usecase/coin" + +type CoinReceivedParams struct { + Address string + Amount coin.Coins +} diff --git a/usecase/model/coin_spent.go b/usecase/model/coin_spent.go new file mode 100644 index 00000000..30a986c1 --- /dev/null +++ b/usecase/model/coin_spent.go @@ -0,0 +1,8 @@ +package model + +import "github.com/crypto-com/chain-indexing/usecase/coin" + +type CoinSpentParams struct { + Address string + Amount coin.Coins +} diff --git a/usecase/parser/block.go b/usecase/parser/block.go index 055ff5a5..cbdf6039 100644 --- a/usecase/parser/block.go +++ b/usecase/parser/block.go @@ -68,6 +68,16 @@ func ParseBlockToCommands( return nil, fmt.Errorf("error parsing block_results txs_results commands: %v", parseErr) } commands = append(commands, txsResultsCommand...) + + blockEventsCommands, parseErr := ParseTxsResultsEventsCommands( + block.Height, + blockResults.TxsResults, + bondingDenom, + ) + if parseErr != nil { + return nil, fmt.Errorf("error parsing events commands: %v", parseErr) + } + commands = append(commands, blockEventsCommands...) } beginBlockEventsCommands, parseErr := ParseBeginBlockEventsCommands( diff --git a/usecase/parser/end_block_events.go b/usecase/parser/end_block_events.go index a5c8103c..67f4a16b 100644 --- a/usecase/parser/end_block_events.go +++ b/usecase/parser/end_block_events.go @@ -70,6 +70,58 @@ func ParseEndBlockEventsCommands(blockHeight int64, endBlockEvents []model.Block EthereumEventVoteRecordId: []byte(ethereumSendToCosmosHandledEvent.MustGetAttributeByKey("ethereum_event_vote_record_id")), }, )) + } else if event.Type == "coin_spent" { + coinSpentEvent := utils.NewParsedTxsResultLogEvent(&endBlockEvents[i]) + + amount := coinSpentEvent.MustGetAttributeByKey("amount") + if amount == "" { + continue + } + commands = append(commands, command_usecase.NewCreateCoinSpent( + blockHeight, + model.CoinSpentParams{ + Address: coinSpentEvent.MustGetAttributeByKey("spender"), + Amount: coin.MustParseCoinsNormalized(amount), + })) + } else if event.Type == "coin_received" { + coinReceivedEvent := utils.NewParsedTxsResultLogEvent(&endBlockEvents[i]) + + amount := coinReceivedEvent.MustGetAttributeByKey("amount") + if amount == "" { + continue + } + commands = append(commands, command_usecase.NewCreateCoinReceived( + blockHeight, + model.CoinReceivedParams{ + Address: coinReceivedEvent.MustGetAttributeByKey("receiver"), + Amount: coin.MustParseCoinsNormalized(amount), + })) + } else if event.Type == "coinbase" { + coinMintEvent := utils.NewParsedTxsResultLogEvent(&endBlockEvents[i]) + + amount := coinMintEvent.MustGetAttributeByKey("amount") + if amount == "" { + continue + } + commands = append(commands, command_usecase.NewCreateCoinMint( + blockHeight, + model.CoinMintParams{ + Address: coinMintEvent.MustGetAttributeByKey("minter"), + Amount: coin.MustParseCoinsNormalized(amount), + })) + } else if event.Type == "burn" { + coinBurnEvent := utils.NewParsedTxsResultLogEvent(&endBlockEvents[i]) + + amount := coinBurnEvent.MustGetAttributeByKey("amount") + if amount == "" { + continue + } + commands = append(commands, command_usecase.NewCreateCoinBurn( + blockHeight, + model.CoinBurnParams{ + Address: coinBurnEvent.MustGetAttributeByKey("burner"), + Amount: coin.MustParseCoinsNormalized(amount), + })) } } diff --git a/usecase/parser/test/end_block_ethereum_send_to_cosmos_handled.go b/usecase/parser/test/end_block_ethereum_send_to_cosmos_handled.go index a482f5d6..fe118a93 100644 --- a/usecase/parser/test/end_block_ethereum_send_to_cosmos_handled.go +++ b/usecase/parser/test/end_block_ethereum_send_to_cosmos_handled.go @@ -366,66 +366,6 @@ const END_BLOCK_ETHEREUM_SEND_TO_COSMOS_HANDLED_BLOCK_RESULTS_RESP = `{ } ] }, - { - "type": "coin_received", - "attributes": [ - { - "key": "cmVjZWl2ZXI=", - "value": "dGNyYzE2bjNsYzdjeXdhNjhtZzUwcWhwODQ3MDM0dzg4cG50cWxhc3U4dQ==", - "index": true - }, - { - "key": "YW1vdW50", - "value": "MjUwZ3Jhdml0eTB4NTY0QTFjM0FGMDg5RDAyRDBCNkMzMTFDNjUwZUEzNzY4NDI0Y2JmYQ==", - "index": true - } - ] - }, - { - "type": "coinbase", - "attributes": [ - { - "key": "bWludGVy", - "value": "dGNyYzE2bjNsYzdjeXdhNjhtZzUwcWhwODQ3MDM0dzg4cG50cWxhc3U4dQ==", - "index": true - }, - { - "key": "YW1vdW50", - "value": "MjUwZ3Jhdml0eTB4NTY0QTFjM0FGMDg5RDAyRDBCNkMzMTFDNjUwZUEzNzY4NDI0Y2JmYQ==", - "index": true - } - ] - }, - { - "type": "coin_spent", - "attributes": [ - { - "key": "c3BlbmRlcg==", - "value": "dGNyYzE2bjNsYzdjeXdhNjhtZzUwcWhwODQ3MDM0dzg4cG50cWxhc3U4dQ==", - "index": true - }, - { - "key": "YW1vdW50", - "value": "MjUwZ3Jhdml0eTB4NTY0QTFjM0FGMDg5RDAyRDBCNkMzMTFDNjUwZUEzNzY4NDI0Y2JmYQ==", - "index": true - } - ] - }, - { - "type": "coin_received", - "attributes": [ - { - "key": "cmVjZWl2ZXI=", - "value": "dGNyYzEzeXV4Nno4bWg2dzV0M3Y0dXE3Y2xld25oMzV6bnJnZGd5ZTBrMg==", - "index": true - }, - { - "key": "YW1vdW50", - "value": "MjUwZ3Jhdml0eTB4NTY0QTFjM0FGMDg5RDAyRDBCNkMzMTFDNjUwZUEzNzY4NDI0Y2JmYQ==", - "index": true - } - ] - }, { "type": "transfer", "attributes": [ @@ -501,36 +441,6 @@ const END_BLOCK_ETHEREUM_SEND_TO_COSMOS_HANDLED_BLOCK_RESULTS_RESP = `{ } ] }, - { - "type": "coin_spent", - "attributes": [ - { - "key": "c3BlbmRlcg==", - "value": "dGNyYzEzeXV4Nno4bWg2dzV0M3Y0dXE3Y2xld25oMzV6bnJnZGd5ZTBrMg==", - "index": true - }, - { - "key": "YW1vdW50", - "value": "MjUwZ3Jhdml0eTB4NTY0QTFjM0FGMDg5RDAyRDBCNkMzMTFDNjUwZUEzNzY4NDI0Y2JmYQ==", - "index": true - } - ] - }, - { - "type": "coin_received", - "attributes": [ - { - "key": "cmVjZWl2ZXI=", - "value": "dGNyYzE2d3c1eWh1MDJzZGM3NGE0MjZndTB3ODAwbGVxa2d3a2xqcHNwZg==", - "index": true - }, - { - "key": "YW1vdW50", - "value": "MjUwZ3Jhdml0eTB4NTY0QTFjM0FGMDg5RDAyRDBCNkMzMTFDNjUwZUEzNzY4NDI0Y2JmYQ==", - "index": true - } - ] - }, { "type": "transfer", "attributes": [ diff --git a/usecase/parser/txs_results_events.go b/usecase/parser/txs_results_events.go new file mode 100644 index 00000000..f1c93d9d --- /dev/null +++ b/usecase/parser/txs_results_events.go @@ -0,0 +1,76 @@ +package parser + +import ( + "github.com/crypto-com/chain-indexing/entity/command" + "github.com/crypto-com/chain-indexing/usecase/coin" + command_usecase "github.com/crypto-com/chain-indexing/usecase/command" + "github.com/crypto-com/chain-indexing/usecase/model" + "github.com/crypto-com/chain-indexing/usecase/parser/utils" +) + +func ParseTxsResultsEventsCommands( + blockHeight int64, + txsResults []model.BlockResultsTxsResult, + bondingDenom string, +) ([]command.Command, error) { + commands := make([]command.Command, 0) + for _, txsResult := range txsResults { + for i, event := range txsResult.Events { + if event.Type == "coin_spent" { + coinSpentEvent := utils.NewParsedTxsResultLogEvent(&txsResult.Events[i]) + + amount := coinSpentEvent.MustGetAttributeByKey("amount") + if amount == "" { + continue + } + commands = append(commands, command_usecase.NewCreateCoinSpent( + blockHeight, + model.CoinSpentParams{ + Address: coinSpentEvent.MustGetAttributeByKey("spender"), + Amount: coin.MustParseCoinsNormalized(amount), + })) + } else if event.Type == "coin_received" { + coinReceivedEvent := utils.NewParsedTxsResultLogEvent(&txsResult.Events[i]) + + amount := coinReceivedEvent.MustGetAttributeByKey("amount") + if amount == "" { + continue + } + commands = append(commands, command_usecase.NewCreateCoinReceived( + blockHeight, + model.CoinReceivedParams{ + Address: coinReceivedEvent.MustGetAttributeByKey("receiver"), + Amount: coin.MustParseCoinsNormalized(amount), + })) + } else if event.Type == "coinbase" { + coinMintEvent := utils.NewParsedTxsResultLogEvent(&txsResult.Events[i]) + + amount := coinMintEvent.MustGetAttributeByKey("amount") + if amount == "" { + continue + } + commands = append(commands, command_usecase.NewCreateCoinMint( + blockHeight, + model.CoinMintParams{ + Address: coinMintEvent.MustGetAttributeByKey("minter"), + Amount: coin.MustParseCoinsNormalized(amount), + })) + } else if event.Type == "burn" { + coinBurnEvent := utils.NewParsedTxsResultLogEvent(&txsResult.Events[i]) + + amount := coinBurnEvent.MustGetAttributeByKey("amount") + if amount == "" { + continue + } + commands = append(commands, command_usecase.NewCreateCoinBurn( + blockHeight, + model.CoinBurnParams{ + Address: coinBurnEvent.MustGetAttributeByKey("burner"), + Amount: coin.MustParseCoinsNormalized(amount), + })) + } + } + } + + return commands, nil +}