From feec418284c38554d64344cfe22ab6f9eb979c1c Mon Sep 17 00:00:00 2001 From: Mocha Date: Thu, 18 May 2023 16:39:30 -0400 Subject: [PATCH] feat: update discord message (#122) --- go.mod | 23 ++-- go.sum | 46 ++++---- pkg/service/alert/service.go | 111 ++++++++---------- pkg/service/alert/service_test.go | 108 +++++++++++++++++ .../integrationtest/transaction_test.go | 2 + pkg/service/processor/position.go | 14 +++ pkg/service/utils/misc.go | 20 ++++ pkg/unittest/utils.go | 8 +- 8 files changed, 235 insertions(+), 97 deletions(-) create mode 100644 pkg/service/alert/service_test.go create mode 100644 pkg/service/utils/misc.go diff --git a/go.mod b/go.mod index d22315cc..c10bcdb3 100644 --- a/go.mod +++ b/go.mod @@ -10,8 +10,8 @@ require ( github.com/dcaf-labs/solana-go-clients v0.0.0-20221202190210-4fa06790e51a github.com/dcaf-labs/solana-go-retryable-http-client v0.0.0-20230416171858-6dc5d47dddc8 github.com/deepmap/oapi-codegen v1.10.1 - github.com/disgoorg/disgo v0.13.19 - github.com/disgoorg/snowflake/v2 v2.0.0 + github.com/disgoorg/disgo v0.16.4 + github.com/disgoorg/snowflake/v2 v2.0.1 github.com/fergusstrange/embedded-postgres v1.21.0 github.com/gagliardetto/metaplex-go v0.2.1 github.com/getkin/kin-openapi v0.94.0 @@ -44,6 +44,7 @@ require ( github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/buger/jsonparser v1.1.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect + github.com/disgoorg/json v1.1.0 // indirect github.com/disgoorg/log v1.2.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect @@ -84,14 +85,14 @@ require ( github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect go.mongodb.org/mongo-driver v1.11.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect - golang.org/x/exp v0.0.0-20220325121720-054d8573a5d8 // indirect - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b // indirect + golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect + golang.org/x/mod v0.8.0 // indirect + golang.org/x/net v0.10.0 // indirect golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect - golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.1.12 // indirect + golang.org/x/tools v0.6.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f // indirect google.golang.org/grpc v1.47.0 // indirect @@ -140,9 +141,9 @@ require ( go.uber.org/fx v1.17.1 go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.23.0 // indirect - golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be // indirect - golang.org/x/sys v0.7.0 // indirect - golang.org/x/term v0.0.0-20220919170432-7a66f970e087 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/term v0.8.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect ) diff --git a/go.sum b/go.sum index 0267db12..34528b8f 100644 --- a/go.sum +++ b/go.sum @@ -422,12 +422,14 @@ github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc/go.mod h1:c9O8+fp github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dhui/dktest v0.3.10 h1:0frpeeoM9pHouHjhLeZDuDTJ0PqjDTrycaHaMmkJAo8= github.com/dhui/dktest v0.3.10/go.mod h1:h5Enh0nG3Qbo9WjNFRrwmKUaePEBhXMOygbz3Ww7Sz0= -github.com/disgoorg/disgo v0.13.19 h1:evbkWRQ9fU3dIrRJnl+jFTt55cKAeeEwnB/Q3dqgz20= -github.com/disgoorg/disgo v0.13.19/go.mod h1:Cyip4bCYHD3rHgDhBPT9cLo81e9AMbDe8ocM50UNRM4= +github.com/disgoorg/disgo v0.16.4 h1:O3qihEjfnJOPcEFqMwcM2h5k1B+B0ac4xj5YV/mT0to= +github.com/disgoorg/disgo v0.16.4/go.mod h1:hUkznOmm+f+owh/MuOBX0sDviQV5cL0FqcWbpIyV1Y0= +github.com/disgoorg/json v1.1.0 h1:7xigHvomlVA9PQw9bMGO02PHGJJPqvX5AnwlYg/Tnys= +github.com/disgoorg/json v1.1.0/go.mod h1:BHDwdde0rpQFDVsRLKhma6Y7fTbQKub/zdGO5O9NqqA= github.com/disgoorg/log v1.2.0 h1:sqlXnu/ZKAlIlHV9IO+dbMto7/hCQ474vlIdMWk8QKo= github.com/disgoorg/log v1.2.0/go.mod h1:3x1KDG6DI1CE2pDwi3qlwT3wlXpeHW/5rVay+1qDqOo= -github.com/disgoorg/snowflake/v2 v2.0.0 h1:+xvyyDddXmXLHmiG8SZiQ3sdZdZPbUR22fSHoqwkrOA= -github.com/disgoorg/snowflake/v2 v2.0.0/go.mod h1:SPU9c2CNn5DSyb86QcKtdZgix9osEtKrHLW4rMhfLCs= +github.com/disgoorg/snowflake/v2 v2.0.1 h1:CuUxGLwggUxEswZOmZ+mZ5i0xSumQdXW9tXW7uGqe+0= +github.com/disgoorg/snowflake/v2 v2.0.1/go.mod h1:SPU9c2CNn5DSyb86QcKtdZgix9osEtKrHLW4rMhfLCs= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= @@ -1328,7 +1330,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -1544,8 +1546,8 @@ golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A= -golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1560,8 +1562,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220325121720-054d8573a5d8 h1:Xt4/LzbTwfocTk9ZLEu4onjeFucl88iW+v4j4PWbQuE= -golang.org/x/exp v0.0.0-20220325121720-054d8573a5d8/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1597,8 +1599,9 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1678,8 +1681,8 @@ golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b h1:uKO3Js8lXGjpjdc4J3rqs0/Ex5yDKUGfk43tTYWVLas= -golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1719,8 +1722,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 h1:cu5kTvlzcw1Q5S9f5ip1/cpiB4nXvw1XYzFPGgzLUOY= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1864,16 +1867,16 @@ golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220919170432-7a66f970e087 h1:tPwmk4vmvVCMdr98VgL4JH+qZxPL8fqlUOHnyOM8N3w= -golang.org/x/term v0.0.0-20220919170432-7a66f970e087/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1882,8 +1885,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1982,8 +1986,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/service/alert/service.go b/pkg/service/alert/service.go index b07f562e..af216623 100644 --- a/pkg/service/alert/service.go +++ b/pkg/service/alert/service.go @@ -7,10 +7,11 @@ import ( "strconv" "time" + "github.com/disgoorg/disgo/rest" + "github.com/dcaf-labs/drip/pkg/service/config" "github.com/dcaf-labs/drip/pkg/service/utils" "github.com/disgoorg/disgo/discord" - "github.com/disgoorg/disgo/rest" "github.com/disgoorg/disgo/webhook" "github.com/disgoorg/snowflake/v2" "github.com/sirupsen/logrus" @@ -29,16 +30,14 @@ func NewAlertService( service := serviceImpl{} if appConfig.GetDiscordWebhookID() != "" && appConfig.GetDiscordWebhookAccessToken() != "" { service = serviceImpl{ - network: appConfig.GetNetwork(), - enabled: true, - discordWebhookID: appConfig.GetDiscordWebhookID(), - discordWebhookAccessToken: appConfig.GetDiscordWebhookAccessToken(), + network: appConfig.GetNetwork(), + enabled: true, } - webhookID, err := strconv.ParseInt(service.discordWebhookID, 10, 64) + webhookID, err := strconv.ParseInt(appConfig.GetDiscordWebhookID(), 10, 64) if err != nil { return nil, err } - client := webhook.New(snowflake.ID(webhookID), service.discordWebhookAccessToken, + client := webhook.New(snowflake.ID(webhookID), appConfig.GetDiscordWebhookAccessToken(), webhook.WithLogger(logrus.New()), webhook.WithDefaultAllowedMentions(discord.AllowedMentions{ RepliedUser: false, @@ -53,11 +52,9 @@ func NewAlertService( } type serviceImpl struct { - network config.Network - enabled bool - client webhook.Client - discordWebhookAccessToken string - discordWebhookID string + network config.Network + enabled bool + client webhook.Client } func (a serviceImpl) SendError(ctx context.Context, err error) error { @@ -97,6 +94,7 @@ type NewPositionAlert struct { NumberOfSwaps uint64 Owner string Position string + USDValue *float64 } func (a serviceImpl) SendNewPositionAlert( @@ -110,24 +108,11 @@ func (a serviceImpl) SendNewPositionAlert( } log.Info("attempting to send notification") - granularityStr := strconv.FormatUint(alertParams.Granularity, 10) - if alertParams.Granularity == 60 { - granularityStr = "Minutely" - } else if alertParams.Granularity == 3600 { - granularityStr = "Hourly" - } else if alertParams.Granularity == 86400 { - granularityStr = "Daily" - } - - tokenA := alertParams.TokenAMint - if alertParams.TokenASymbol != nil && *alertParams.TokenASymbol != "" { - tokenA = *alertParams.TokenASymbol - } + granularityStr := getGranularityString(alertParams.Granularity) + tokenA := utils.GetWithDefault(alertParams.TokenASymbol, alertParams.TokenAMint) + tokenB := utils.GetWithDefault(alertParams.TokenBSymbol, alertParams.TokenBMint) + usdValue := utils.GetWithDefault(alertParams.USDValue, 0) - tokenB := alertParams.TokenBMint - if alertParams.TokenBSymbol != nil && *alertParams.TokenBSymbol != "" { - tokenB = *alertParams.TokenBSymbol - } inLineTrue := utils.GetBoolPtr(true) embed := discord.NewEmbedBuilder(). SetTitle("New Position!"). @@ -140,45 +125,47 @@ func (a serviceImpl) SendNewPositionAlert( discord.EmbedField{Name: "Granularity", Value: granularityStr, Inline: inLineTrue}, discord.EmbedField{Name: "Drip Amount", Value: strconv.FormatFloat(alertParams.ScaledDripAmount, 'f', -1, 32), Inline: inLineTrue}, discord.EmbedField{Name: "Number of swaps", Value: strconv.FormatUint(alertParams.NumberOfSwaps, 10), Inline: inLineTrue}, + discord.EmbedField{Name: "USD Value", Value: strconv.FormatFloat(usdValue, 'f', -1, 32)}, discord.EmbedField{Name: "Owner", Value: alertParams.Owner}, ). Build() embeds := []discord.Embed{embed} - if alertParams.TokenAIconURL != nil && *alertParams.TokenAIconURL != "" && alertParams.TokenASymbol != nil && *alertParams.TokenASymbol != "" { - tokenAEmbed := discord.NewEmbedBuilder(). - SetTitle("TokenA"). - SetColor(int(SuccessColor)). - SetURL(a.getExplorerURL(alertParams.TokenAMint)). - SetFields( - discord.EmbedField{Name: "Symbol", Value: *alertParams.TokenASymbol}, - ). - SetEmbedFooter(&discord.EmbedFooter{ - Text: alertParams.TokenAMint, - IconURL: *alertParams.TokenAIconURL, - ProxyIconURL: "", - }). - Build() - - embeds = append(embeds, tokenAEmbed) - } - if alertParams.TokenBIconURL != nil && *alertParams.TokenBIconURL != "" && alertParams.TokenBSymbol != nil && *alertParams.TokenBSymbol != "" { - tokenBEmbed := discord.NewEmbedBuilder(). - SetTitle("TokenB"). - SetColor(int(SuccessColor)). - SetURL(a.getExplorerURL(alertParams.TokenBMint)). - SetFields( - discord.EmbedField{Name: "Symbol", Value: *alertParams.TokenBSymbol}, - ). - SetEmbedFooter(&discord.EmbedFooter{ - Text: alertParams.TokenBMint, - IconURL: *alertParams.TokenBIconURL, - ProxyIconURL: "", - }). - Build() - embeds = append(embeds, tokenBEmbed) - } + + tokenAEmbed := discord.NewEmbedBuilder(). + SetURL(a.getExplorerURL(alertParams.Position)). + SetImage(utils.GetWithDefault(alertParams.TokenAIconURL, "https://pbs.twimg.com/profile_images/1512938686702403603/DDObiFjj_400x400.jpg")). + Build() + embeds = append(embeds, tokenAEmbed) + + tokenBEmbed := discord.NewEmbedBuilder(). + SetTitle("TokenB"). + SetColor(int(SuccessColor)). + SetURL(a.getExplorerURL(alertParams.Position)). + SetImage(utils.GetWithDefault(alertParams.TokenBIconURL, "https://pbs.twimg.com/profile_images/1512938686702403603/DDObiFjj_400x400.jpg")). + Build() + embeds = append(embeds, tokenBEmbed) + return a.send(ctx, embeds...) } +func getGranularityString(granularity uint64) (granularityStr string) { + minuteInS := uint64(time.Minute.Seconds()) + hourInS := uint64(time.Hour.Seconds()) + dayInS := uint64((time.Hour * 24).Seconds()) + if uint64(granularity/minuteInS) <= 1 { + granularityStr = "Every Minute" + } else if granularity > minuteInS && granularity < hourInS { + granularityStr = fmt.Sprintf("Every %d Minutes", uint64(granularity/minuteInS)) + } else if uint64(granularity/hourInS) <= 1 { + granularityStr = "Every Hour" + } else if granularity > hourInS && granularity < dayInS { + granularityStr = fmt.Sprintf("Every %d Hours", uint64(granularity/hourInS)) + } else if uint64(granularity/dayInS) <= 1 { + granularityStr = "Every Day" + } else { + granularityStr = fmt.Sprintf("Every %d Days", uint64(granularity/dayInS)) + } + return granularityStr +} func (a serviceImpl) send(ctx context.Context, embeds ...discord.Embed) error { _, err := a.client.CreateMessage( diff --git a/pkg/service/alert/service_test.go b/pkg/service/alert/service_test.go new file mode 100644 index 00000000..1c3f410a --- /dev/null +++ b/pkg/service/alert/service_test.go @@ -0,0 +1,108 @@ +package alert + +import ( + "context" + "os" + "strconv" + "testing" + + "github.com/AlekSi/pointer" + "github.com/dcaf-labs/drip/pkg/service/config" + "github.com/disgoorg/disgo/discord" + "github.com/disgoorg/disgo/webhook" + "github.com/disgoorg/snowflake/v2" + "github.com/sirupsen/logrus" + "github.com/test-go/testify/assert" +) + +func Test_SendNewPositionAlert(t *testing.T) { + t.Skip("skipping test...") + + config.LoadEnv() + + t.Run("should send position alert", func(t *testing.T) { + webhookID, err := strconv.ParseInt(os.Getenv("DISCORD_WEBHOOK_ID"), 10, 64) + assert.NoError(t, err) + accessToken := os.Getenv("DISCORD_ACCESS_TOKEN") + client := webhook.New(snowflake.ID(webhookID), accessToken, + webhook.WithLogger(logrus.New()), + webhook.WithDefaultAllowedMentions(discord.AllowedMentions{ + RepliedUser: false, + }), + ) + newAlertService := serviceImpl{ + network: config.MainnetNetwork, + enabled: true, + client: client, + } + ctx := context.Background() + assert.NoError(t, + newAlertService.SendNewPositionAlert(ctx, NewPositionAlert{ + TokenASymbol: pointer.ToString("SAMO"), + TokenAIconURL: pointer.ToString("https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU/logo.png"), + TokenAMint: "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU", + TokenBSymbol: pointer.ToString("USDC"), + TokenBIconURL: pointer.ToString("https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v/logo.png "), + TokenBMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + ScaledTokenADepositAmount: 10000, + ScaledDripAmount: 10, + Granularity: 400, + NumberOfSwaps: 45, + Owner: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + Position: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + USDValue: pointer.ToFloat64(100), + })) + }) + + t.Run("should return formatted granularity from getGranularityString", func(t *testing.T) { + tests := []struct { + input uint64 + output string + }{ + { + input: 10, + output: "Every Minute", + }, + { + input: 60, + output: "Every Minute", + }, + { + input: 90, + output: "Every Minute", + }, + { + input: 110, + output: "Every Minute", + }, + { + input: 120, + output: "Every 2 Minutes", + }, + { + input: 3600, + output: "Every Hour", + }, + { + input: 4000, + output: "Every Hour", + }, + { + input: 86400, + output: "Every Day", + }, + { + input: 120400, + output: "Every Day", + }, + { + input: 172800, + output: "Every 2 Days", + }, + } + + for _, testCase := range tests { + assert.Equal(t, testCase.output, getGranularityString(testCase.input)) + } + }) +} diff --git a/pkg/service/processor/integrationtest/transaction_test.go b/pkg/service/processor/integrationtest/transaction_test.go index bb81f3c9..3518bd1a 100644 --- a/pkg/service/processor/integrationtest/transaction_test.go +++ b/pkg/service/processor/integrationtest/transaction_test.go @@ -54,6 +54,7 @@ func Test_ProcessTransaction(t *testing.T) { Time: time.Unix(1680117259, 0), Vault: "BmJs2b1PnepHwBaiWqzuX8LywBLkbBymqj7Cpjz5WjuY", Referrer: pointer.ToString("9d1wBhpKd24y1XwqawL3WWUjw14H7JqB9jcqkrhB3eHW"), + Depositor: pointer.ToString("9sH27VRjiheq9JDH3UNLkUxLjdPSms2irg7UN9H6itxG"), TokenADepositAmount: uint64(200000000000), TokenAUsdPriceDay: nil, }) @@ -208,6 +209,7 @@ func Test_ProcessTransaction(t *testing.T) { Referrer: nil, TokenADepositAmount: uint64(100000000), TokenAUsdPriceDay: nil, + Depositor: pointer.ToString("4ibRFa5k7f4nDxaaBbp8DQYAcMWPHoLSC5raCgSWXExC"), }) }) }) diff --git a/pkg/service/processor/position.go b/pkg/service/processor/position.go index ccd945ff..a1443f74 100644 --- a/pkg/service/processor/position.go +++ b/pkg/service/processor/position.go @@ -6,6 +6,8 @@ import ( "math" "time" + "github.com/AlekSi/pointer" + "github.com/dcaf-labs/drip/pkg/service/alert" "github.com/dcaf-labs/drip/pkg/service/repository" "github.com/dcaf-labs/drip/pkg/service/repository/model" @@ -128,6 +130,17 @@ func (p impl) sendNewPositionAlert(ctx context.Context, positionAddr string) err scaledTokenADepositAmount := float64(position.DepositedTokenAAmount) / math.Pow(10, float64(tokenA.Decimals)) scaledPeriodicDripAmount := float64(position.PeriodicDripAmount) / math.Pow(10, float64(tokenA.Decimals)) + usdValue := 0.0 + if tokenA.CoinGeckoID != nil { + prices, err := p.coinGeckoClient.GetMarketPriceForTokens(ctx, *tokenA.CoinGeckoID) + if err != nil { + logrus.WithError(err).WithField("position", position.Pubkey).Error("failed to get position pricing") + } + if len(prices) == 1 { + usdValue = prices[0].CurrentPrice * scaledTokenADepositAmount + } + } + newPositionAlert := alert.NewPositionAlert{ TokenASymbol: tokenA.Symbol, TokenAIconURL: tokenA.IconURL, @@ -141,6 +154,7 @@ func (p impl) sendNewPositionAlert(ctx context.Context, positionAddr string) err NumberOfSwaps: position.NumberOfSwaps, Owner: owner, Position: positionAddr, + USDValue: pointer.ToFloat64(usdValue), } return p.alertService.SendNewPositionAlert(ctx, newPositionAlert) diff --git a/pkg/service/utils/misc.go b/pkg/service/utils/misc.go new file mode 100644 index 00000000..2c2f2d58 --- /dev/null +++ b/pkg/service/utils/misc.go @@ -0,0 +1,20 @@ +package utils + +/* +GetWithDefault - Return value if its not nil, else return a fallback +ex: +``` +const value := "my value" +var nilValue *string = nil +const fallback = "my fallback" + +assert.Equal(t, GetWithDefault(&value, fallback), value) +assert.Equal(t, GetWithDefault(nilValue, fallback), fallback) +``` +*/ +func GetWithDefault[A any](value *A, fallBack A) A { + if value != nil { + return *value + } + return fallBack +} diff --git a/pkg/unittest/utils.go b/pkg/unittest/utils.go index 88c0520d..ed71104b 100644 --- a/pkg/unittest/utils.go +++ b/pkg/unittest/utils.go @@ -8,6 +8,8 @@ import ( "net/http/httptest" "os" + "github.com/gagliardetto/solana-go/rpc" + "github.com/dcaf-labs/drip/pkg/service/config" api "github.com/dcaf-labs/solana-go-retryable-http-client" "github.com/golang/mock/gomock" @@ -38,7 +40,7 @@ func GetMockMainnetProductionConfig(ctrl *gomock.Controller) *config.MockAppConf mockConfig.EXPECT().GetEnvironment().Return(config.ProductionEnv).AnyTimes() mockConfig.EXPECT().GetDripProgramID().Return("dripTrkvSyQKvkyWg7oi4jmeEGMA5scSYowHArJ9Vwk").AnyTimes() mockConfig.EXPECT().GetServerPort().Return(8080).AnyTimes() - mockConfig.EXPECT().GetSolanaRPCURL().AnyTimes().Return("https://palpable-warmhearted-hexagon.solana-mainnet.discover.quiknode.pro/5793cf44e6e16325347e62d571454890f16e0388/") + mockConfig.EXPECT().GetSolanaRPCURL().AnyTimes().Return(rpc.MainNetBeta.RPC) mockConfig.EXPECT().GetSolanaWSURL().AnyTimes().Return("wss://palpable-warmhearted-hexagon.solana-mainnet.discover.quiknode.pro/5793cf44e6e16325347e62d571454890f16e0388/") return mockConfig } @@ -113,6 +115,6 @@ func requestMatcher(r *http.Request, i cassette.Request) bool { } r.Body.Close() r.Body = ioutil.NopCloser(bytes.NewBuffer(reqBody)) - - return r.Method == i.Method && r.URL.String() == i.URL && string(reqBody) == i.Body + // TODO: Do we need to check the exact url? + return r.Method == i.Method && r.RequestURI == i.RequestURI && string(reqBody) == i.Body }