From 07e25eb118ab5934db43d1e309e33a2e51bfc9de Mon Sep 17 00:00:00 2001 From: dreamer Date: Sun, 14 Apr 2024 09:51:11 +0800 Subject: [PATCH 1/5] implement DeployERC20 by gov proposal --- go.mod | 12 ++++ go.sum | 22 +++++++ modules/token/depinject.go | 6 ++ modules/token/keeper/erc20.go | 75 +++++++++++++++++++++ modules/token/keeper/evm.go | 88 +++++++++++++++++++++++++ modules/token/keeper/keeper.go | 26 ++++++++ modules/token/keeper/msg_server.go | 50 ++++++++++++-- modules/token/keeper/params.go | 4 +- modules/token/keeper/token.go | 40 +++++++++-- modules/token/types/errors.go | 6 ++ modules/token/types/expected_keepers.go | 30 ++++++++- modules/token/types/keys.go | 18 +++-- modules/token/types/v1/msgs.go | 42 +++++++++++- 13 files changed, 399 insertions(+), 20 deletions(-) create mode 100644 modules/token/keeper/erc20.go create mode 100644 modules/token/keeper/evm.go diff --git a/go.mod b/go.mod index b841936f..7c1ad6d4 100644 --- a/go.mod +++ b/go.mod @@ -43,6 +43,8 @@ require ( github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect + github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect + github.com/VictoriaMetrics/fastcache v1.6.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/aws/aws-sdk-go v1.44.203 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -68,6 +70,7 @@ require ( github.com/creachadair/taskgroup v0.3.2 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/deckarep/golang-set v1.8.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect @@ -83,6 +86,7 @@ require ( github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/googleapis v1.4.1 // indirect @@ -112,6 +116,7 @@ require ( github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hdevalence/ed25519consensus v0.1.0 // indirect + github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/holiman/uint256 v1.2.0 // indirect github.com/huandu/skiplist v1.2.0 // indirect github.com/improbable-eng/grpc-web v0.15.0 // indirect @@ -127,6 +132,7 @@ require ( github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect github.com/minio/highwayhash v1.0.2 // indirect @@ -134,6 +140,7 @@ require ( github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mtibben/percent v0.2.1 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.7 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect @@ -143,11 +150,13 @@ require ( github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect + github.com/prometheus/tsdb v0.7.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/rs/cors v1.8.2 // indirect github.com/rs/zerolog v1.32.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/spf13/afero v1.9.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/viper v1.14.0 // indirect @@ -158,6 +167,8 @@ require ( github.com/tidwall/btree v1.6.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect + github.com/tklauser/go-sysconf v0.3.5 // indirect + github.com/tklauser/numcpus v0.2.2 // indirect github.com/ulikunitz/xz v0.5.11 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -181,6 +192,7 @@ require ( google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect nhooyr.io/websocket v1.8.6 // indirect pgregory.net/rapid v1.1.0 // indirect diff --git a/go.sum b/go.sum index 18df2a5f..07db6765 100644 --- a/go.sum +++ b/go.sum @@ -220,7 +220,9 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= +github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= @@ -230,6 +232,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -360,6 +364,8 @@ github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnG github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= +github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= @@ -373,6 +379,7 @@ github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkz github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -384,6 +391,7 @@ github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -442,6 +450,7 @@ github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= @@ -657,6 +666,7 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU= github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -747,6 +757,7 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -794,8 +805,10 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= @@ -862,6 +875,7 @@ github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= @@ -879,6 +893,7 @@ github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= @@ -907,6 +922,7 @@ github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71e github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -976,7 +992,9 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= +github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= +github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= @@ -1262,7 +1280,9 @@ golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1653,6 +1673,8 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= diff --git a/modules/token/depinject.go b/modules/token/depinject.go index 00f2da08..19ee134b 100644 --- a/modules/token/depinject.go +++ b/modules/token/depinject.go @@ -45,6 +45,8 @@ type TokenInputs struct { AccountKeeper types.AccountKeeper BankKeeper types.BankKeeper + EVMKeeper types.EVMKeeper + ICS20Keeper types.ICS20Keeper // LegacySubspace is used solely for migration of x/params managed parameters LegacySubspace exported.Subspace `optional:"true"` @@ -55,6 +57,7 @@ type TokenOutputs struct { TokenKeeper keeper.Keeper Module appmodule.AppModule + } func ProvideModule(in TokenInputs) TokenOutputs { @@ -68,6 +71,9 @@ func ProvideModule(in TokenInputs) TokenOutputs { in.Cdc, in.Key, in.BankKeeper, + in.AccountKeeper, + in.EVMKeeper, + in.ICS20Keeper, in.Config.FeeCollectorName, authority.String(), ) diff --git a/modules/token/keeper/erc20.go b/modules/token/keeper/erc20.go new file mode 100644 index 00000000..13865358 --- /dev/null +++ b/modules/token/keeper/erc20.go @@ -0,0 +1,75 @@ +package keeper + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/irisnet/irismod/contracts" + "github.com/irisnet/irismod/modules/token/types" + v1 "github.com/irisnet/irismod/modules/token/types/v1" +) + +// DeployERC20 deploys an ERC20 token contract. +// +// Parameters: +// - ctx: the context +// - name: the name of the token +// - symbol: the symbol of the token +// - minUnit: the symbol of the minUnit +// - scale: the scale of the token +// +// Returns: +// - Address: the contract address. +// - error: error if any. +func (k Keeper) DeployERC20( + ctx sdk.Context, + name string, + symbol string, + minUnit string, + scale int8, +) (common.Address, error) { + contractArgs, err := contracts.ERC20TokenContract.ABI.Pack( + "", + name, + symbol, + scale, + ) + if err != nil { + return common.Address{}, errorsmod.Wrapf(types.ErrABIPack, "erc20 metadata is invalid %s: %s", name, err.Error()) + } + deployer := k.moduleAddress() + + data := make([]byte, len(contracts.ERC20TokenContract.Bin)+len(contractArgs)) + copy(data[:len(contracts.ERC20TokenContract.Bin)], contracts.ERC20TokenContract.Bin) + copy(data[len(contracts.ERC20TokenContract.Bin):], contractArgs) + + nonce, err := k.accountKeeper.GetSequence(ctx, sdk.AccAddress(deployer.Bytes())) + if err != nil { + return common.Address{}, err + } + contractAddr := crypto.CreateAddress(deployer, nonce) + result, err := k.CallEVMWithData(ctx, deployer, nil, data, true) + if err != nil { + return common.Address{}, errorsmod.Wrapf(err, "failed to deploy contract for token %s", name) + } + if result.Failed() { + return common.Address{}, errorsmod.Wrapf(types.ErrVMExecution, "failed to deploy contract for %s, reason: %s", name, result.Revert()) + } + + ctx.EventManager().EmitTypedEvent(&v1.EventDeployERC20{ + Symbol: symbol, + Name: name, + Scale: uint32(scale), + MinUnit: minUnit, + Contract: contractAddr.String(), + }) + return contractAddr, nil +} + + +func (k Keeper) moduleAddress() common.Address { + moduleAddr := k.accountKeeper.GetModuleAddress(types.ModuleName) + return common.BytesToAddress(moduleAddr.Bytes()) +} \ No newline at end of file diff --git a/modules/token/keeper/evm.go b/modules/token/keeper/evm.go new file mode 100644 index 00000000..0a7eb2f4 --- /dev/null +++ b/modules/token/keeper/evm.go @@ -0,0 +1,88 @@ +package keeper + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + ethtypes "github.com/ethereum/go-ethereum/core/types" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + + tokentypes "github.com/irisnet/irismod/modules/token/types" + "github.com/irisnet/irismod/types" +) + +// CallEVMWithData executes an Ethereum Virtual Machine (EVM) call with the provided data. +// +// Parameters: +// - ctx: the context in which the EVM call is executed +// - from: the address initiating the EVM call +// - contract: the address of the smart contract +// - data: the data to be sent with the EVM call +// - commit: boolean indicating whether the EVM call should be committed +// +// Returns: +// - *types.Result: the result of the EVM call +// - error: an error if the EVM call encounters any issues +func (k Keeper) CallEVMWithData( + ctx sdk.Context, + from common.Address, + contract *common.Address, + data []byte, + commit bool, +) (*types.Result, error) { + nonce, err := k.accountKeeper.GetSequence(ctx, from.Bytes()) + if err != nil { + return nil, err + } + + gasCap := types.DefaultGasCap + if commit { + args, err := json.Marshal(types.TransactionArgs{ + From: &from, + To: contract, + Data: (*hexutil.Bytes)(&data), + }) + if err != nil { + return nil, errorsmod.Wrapf(tokentypes.ErrJSONMarshal, "failed to marshal tx args: %s", err.Error()) + } + + gas, err := k.evmKeeper.EstimateGas(sdk.WrapSDKContext(ctx), &types.EthCallRequest{ + Args: args, + GasCap: types.DefaultGasCap, + ChainID: k.evmKeeper.ChainID().Int64(), + }) + if err != nil { + return nil, err + } + gasCap = gas + } + + msg := ethtypes.NewMessage( + from, + contract, + nonce, + big.NewInt(0), // amount + gasCap, // gasLimit + big.NewInt(0), // gasFeeCap + big.NewInt(0), // gasTipCap + big.NewInt(0), // gasPrice + data, + ethtypes.AccessList{}, // AccessList + !commit, // isFake + ) + + res, err := k.evmKeeper.ApplyMessage(ctx, msg, types.NewNoOpTracer(), commit) + if err != nil { + return nil, err + } + + if res.Failed() { + return nil, errorsmod.Wrap(tokentypes.ErrVMExecution, res.VMError) + } + + return res, nil +} \ No newline at end of file diff --git a/modules/token/keeper/keeper.go b/modules/token/keeper/keeper.go index 49632656..296920f9 100644 --- a/modules/token/keeper/keeper.go +++ b/modules/token/keeper/keeper.go @@ -15,20 +15,39 @@ import ( v1 "github.com/irisnet/irismod/modules/token/types/v1" ) +// Keeper of the token store type Keeper struct { storeKey storetypes.StoreKey cdc codec.Codec bankKeeper types.BankKeeper + accountKeeper types.AccountKeeper + evmKeeper types.EVMKeeper + ics20Keeper types.ICS20Keeper blockedAddrs map[string]bool feeCollectorName string authority string registry v1.SwapRegistry } +// NewKeeper creates a new instance of Keeper. +// +// Parameters: +// cdc: codec to marshal/unmarshal binary encoding/decoding. +// key: store key for the module's store. +// bankKeeper: bank Keeper module for interacting with accounts. +// accountKeeper: Account Keeper for interacting with accounts. +// evmKeeper: EVM Keeper module for interacting with Ethereum Virtual Machine transactions. +// ics20Keeper: ICS20 Keeper module for interacting with ICS20 transactions. +// feeCollectorName: name of the fee collector. +// authority: authority string. +// Return type: Keeper. func NewKeeper( cdc codec.Codec, key storetypes.StoreKey, bankKeeper types.BankKeeper, + accountKeeper types.AccountKeeper, + evmKeeper types.EVMKeeper, + ics20Keeper types.ICS20Keeper, feeCollectorName string, authority string, ) Keeper { @@ -36,6 +55,9 @@ func NewKeeper( storeKey: key, cdc: cdc, bankKeeper: bankKeeper, + accountKeeper: accountKeeper, + evmKeeper: evmKeeper, + ics20Keeper: ics20Keeper, feeCollectorName: feeCollectorName, blockedAddrs: bankKeeper.GetBlockedAddresses(), registry: make(v1.SwapRegistry), @@ -294,6 +316,10 @@ func (k Keeper) SwapFeeToken( ) } +// WithSwapRegistry sets the swap registry in the Keeper and returns the updated Keeper instance. +// +// registry: The swap registry to set. +// Returns the updated Keeper instance. func (k Keeper) WithSwapRegistry(registry v1.SwapRegistry) Keeper { k.registry = registry return k diff --git a/modules/token/keeper/msg_server.go b/modules/token/keeper/msg_server.go index 51b26944..5609b1dc 100644 --- a/modules/token/keeper/msg_server.go +++ b/modules/token/keeper/msg_server.go @@ -15,7 +15,6 @@ type msgServer struct { k Keeper } - var _ v1.MsgServer = msgServer{} // NewMsgServerImpl returns an implementation of the token MsgServer interface @@ -272,8 +271,50 @@ func (m msgServer) UpdateParams( } // DeployERC20 implements v1.MsgServer. -func (m msgServer) DeployERC20(context.Context, *v1.MsgDeployERC20) (*v1.MsgDeployERC20Response, error) { - panic("unimplemented") +func (m msgServer) DeployERC20(goCtx context.Context, msg *v1.MsgDeployERC20) (*v1.MsgDeployERC20Response, error) { + var ( + ctx = sdk.UnwrapSDKContext(goCtx) + name = msg.Name + symbol = msg.Symbol + scale = msg.Scale + minUnit = msg.MinUnit + token v1.Token + err error + ) + + if !m.k.HasMinUint(ctx, msg.MinUnit) { + if !m.k.ics20Keeper.HasTrace(ctx, msg.MinUnit) { + return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "token: %s not exist", msg.MinUnit) + } + token = v1.Token{ + Symbol: symbol, + Name: name, + Scale: scale, + MinUnit: msg.MinUnit, + Mintable: true, + Owner: m.k.accountKeeper.GetModuleAddress(types.ModuleName).String(), + } + } else { + token, err = m.k.getTokenByMinUnit(ctx, msg.MinUnit) + if err != nil { + return nil, err + } + if len(token.Contract) > 0 { + return nil, errorsmod.Wrapf(types.ErrERC20AlreadyExists, "token: %s already deployed erc20 contract: %s", token.Symbol, token.Contract) + } + name = token.Name + symbol = token.Symbol + scale = token.Scale + minUnit = token.MinUnit + } + + contractAddr, err := m.k.DeployERC20(ctx, name, symbol, minUnit, int8(scale)) + if err != nil { + return nil, err + } + token.Contract = contractAddr.String() + m.k.upsertToken(ctx, token) + return &v1.MsgDeployERC20Response{}, nil } // SwapFromERC20 implements v1.MsgServer. @@ -284,5 +325,4 @@ func (m msgServer) SwapFromERC20(context.Context, *v1.MsgSwapFromERC20) (*v1.Msg // SwapToERC20 implements v1.MsgServer. func (m msgServer) SwapToERC20(context.Context, *v1.MsgSwapToERC20) (*v1.MsgSwapToERC20Response, error) { panic("unimplemented") -} - +} \ No newline at end of file diff --git a/modules/token/keeper/params.go b/modules/token/keeper/params.go index b0a68604..5979d5bc 100644 --- a/modules/token/keeper/params.go +++ b/modules/token/keeper/params.go @@ -10,7 +10,7 @@ import ( // GetParams sets the token module parameters. func (k Keeper) GetParams(ctx sdk.Context) (params v1.Params) { store := ctx.KVStore(k.storeKey) - bz := store.Get([]byte(types.ParamsKey)) + bz := store.Get([]byte(types.PrefixParamsKey)) if bz == nil { return params } @@ -30,7 +30,7 @@ func (k Keeper) SetParams(ctx sdk.Context, params v1.Params) error { if err != nil { return err } - store.Set(types.ParamsKey, bz) + store.Set(types.PrefixParamsKey, bz) return nil } diff --git a/modules/token/keeper/token.go b/modules/token/keeper/token.go index 278b0b2a..1529c16e 100644 --- a/modules/token/keeper/token.go +++ b/modules/token/keeper/token.go @@ -114,14 +114,18 @@ func (k Keeper) HasSymbol(ctx sdk.Context, symbol string) bool { return store.Has(types.KeySymbol(symbol)) } +// HasMinUint asserts a token exists by minUint +func (k Keeper) HasMinUint(ctx sdk.Context, minUint string) bool { + store := ctx.KVStore(k.storeKey) + return store.Has(types.KeyMinUint(minUint)) +} + // HasToken asserts a token exists func (k Keeper) HasToken(ctx sdk.Context, denom string) bool { - store := ctx.KVStore(k.storeKey) if k.HasSymbol(ctx, denom) { return true } - - return store.Has(types.KeyMinUint(denom)) + return k.HasMinUint(ctx, denom) } // GetOwner returns the owner of the specified token @@ -173,7 +177,7 @@ func (k Keeper) GetAllBurnCoin(ctx sdk.Context) []sdk.Coin { store := ctx.KVStore(k.storeKey) var coins []sdk.Coin - it := sdk.KVStorePrefixIterator(store, types.PeffixBurnTokenAmt) + it := sdk.KVStorePrefixIterator(store, types.PrefixBurnTokenAmt) for ; it.Valid(); it.Next() { var coin sdk.Coin k.cdc.MustUnmarshal(it.Value(), &coin) @@ -199,6 +203,14 @@ func (k Keeper) setWithMinUnit(ctx sdk.Context, minUnit, symbol string) { store.Set(types.KeyMinUint(minUnit), bz) } +func (k Keeper) setWithContract(ctx sdk.Context, contract, symbol string) { + store := ctx.KVStore(k.storeKey) + + bz := k.cdc.MustMarshal(&gogotypes.StringValue{Value: symbol}) + + store.Set(types.KeyContract(contract), bz) +} + func (k Keeper) setToken(ctx sdk.Context, token v1.Token) { store := ctx.KVStore(k.storeKey) bz := k.cdc.MustMarshal(&token) @@ -279,3 +291,23 @@ func (k Keeper) resetStoreKeyForQueryToken( func (k Keeper) getTokenSupply(ctx sdk.Context, denom string) sdk.Int { return k.bankKeeper.GetSupply(ctx, denom).Amount } + + +// upsertToken updates or inserts a token into the database. +// +// ctx: the context in which the token is being upserted. +// token: the token struct to be upserted. +func (k Keeper) upsertToken(ctx sdk.Context, token v1.Token) { + // set token + k.setToken(ctx, token) + // set token to be prefixed with min unit + k.setWithMinUnit(ctx, token.MinUnit, token.Symbol) + if len(token.Owner) != 0 { + // set token to be prefixed with owner + k.setWithOwner(ctx, token.GetOwner(), token.Symbol) + } + if len(token.Contract) != 0 { + // set token to be prefixed with owner + k.setWithContract(ctx, token.Contract, token.Symbol) + } +} diff --git a/modules/token/types/errors.go b/modules/token/types/errors.go index af557a2a..bebdc969 100644 --- a/modules/token/types/errors.go +++ b/modules/token/types/errors.go @@ -24,4 +24,10 @@ var ( ErrInvalidBaseFee = errorsmod.Register(ModuleName, 16, "invalid base fee") ErrInvalidSwap = errorsmod.Register(ModuleName, 17, "unregistered swapable fee token") ErrInsufficientFee = errorsmod.Register(ModuleName, 18, "the amount of tokens after swap is less than 1") + ErrJSONMarshal = errorsmod.Register(ModuleName, 19, "failed to marshal JSON bytes") + ErrVMExecution = errorsmod.Register(ModuleName, 20, "evm transaction execution failed") + ErrABIPack = errorsmod.Register(ModuleName, 21, "contract ABI pack failed") + ErrERC20AlreadyExists = errorsmod.Register(ModuleName, 22, "erc20 contract already exists") + ErrERC20NotDeployed = errorsmod.Register(ModuleName, 23, "erc20 contract not deployed") + ErrUnsupportedKey = errorsmod.Register(ModuleName, 24, "evm not supported public key") ) diff --git a/modules/token/types/expected_keepers.go b/modules/token/types/expected_keepers.go index e5922c19..84312491 100644 --- a/modules/token/types/expected_keepers.go +++ b/modules/token/types/expected_keepers.go @@ -1,9 +1,18 @@ package types import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/vm" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + "github.com/irisnet/irismod/types" ) // BankKeeper defines the expected bank keeper (noalias) @@ -41,7 +50,22 @@ type BankKeeper interface { // AccountKeeper defines the expected account keeper type AccountKeeper interface { - GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI - GetModuleAddress(name string) sdk.AccAddress - GetModuleAccount(ctx sdk.Context, name string) authtypes.ModuleAccountI + GetModuleAddress(moduleName string) sdk.AccAddress + GetSequence(sdk.Context, sdk.AccAddress) (uint64, error) + GetAccount(sdk.Context, sdk.AccAddress) authtypes.AccountI +} + +// EVMKeeper defines the expected keeper of the evm module +type EVMKeeper interface { + FeeDenom() string + ChainID() *big.Int + + SupportedKey(pubKey cryptotypes.PubKey) bool + EstimateGas(ctx context.Context, req *types.EthCallRequest) (uint64, error) + ApplyMessage(ctx sdk.Context, msg core.Message, tracer vm.EVMLogger, commit bool) (*types.Result, error) +} + +// ICS20Keeper defines the expected keeper of ICS20 +type ICS20Keeper interface{ + HasTrace(ctx sdk.Context, denom string) bool } diff --git a/modules/token/types/keys.go b/modules/token/types/keys.go index 9b7cf196..4cebbcc0 100644 --- a/modules/token/types/keys.go +++ b/modules/token/types/keys.go @@ -2,6 +2,7 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" ) const ( @@ -28,9 +29,12 @@ var ( PrefixTokenForMinUint = []byte{0x02} // PrefixTokens defines a prefix for the tokens PrefixTokens = []byte{0x03} - // PeffixBurnTokenAmt defines a prefix for the amount of token burnt - PeffixBurnTokenAmt = []byte{0x04} - ParamsKey = []byte{0x05} // prefix for the token params + // PrefixBurnTokenAmt defines a prefix for the amount of token burnt + PrefixBurnTokenAmt = []byte{0x04} + // PrefixParamsKey defines the key for the Params store + PrefixParamsKey = []byte{0x05} + // PrefixTokenForContract defines the erc20 contract prefix for the token + PrefixTokenForContract = []byte{0x06} ) // KeySymbol returns the key of the token with the specified symbol @@ -43,6 +47,12 @@ func KeyMinUint(minUnit string) []byte { return append(PrefixTokenForMinUint, []byte(minUnit)...) } +// KeyContract returns the key of the token with the specified contract +func KeyContract(contract string) []byte { + bz := common.FromHex(contract) + return append(PrefixTokenForContract, bz...) +} + // KeyTokens returns the key of the specified owner and symbol. Intended for querying all tokens of an owner func KeyTokens(owner sdk.AccAddress, symbol string) []byte { return append(append(PrefixTokens, owner.Bytes()...), []byte(symbol)...) @@ -50,5 +60,5 @@ func KeyTokens(owner sdk.AccAddress, symbol string) []byte { // KeyBurnTokenAmt returns the key of the specified min unit. func KeyBurnTokenAmt(minUint string) []byte { - return append(PeffixBurnTokenAmt, []byte(minUint)...) + return append(PrefixBurnTokenAmt, []byte(minUint)...) } diff --git a/modules/token/types/v1/msgs.go b/modules/token/types/v1/msgs.go index 3e2378bb..f1d39484 100644 --- a/modules/token/types/v1/msgs.go +++ b/modules/token/types/v1/msgs.go @@ -1,6 +1,9 @@ package v1 import ( + fmt "fmt" + "regexp" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -33,6 +36,10 @@ var ( _ sdk.Msg = &MsgDeployERC20{} _ sdk.Msg = &MsgSwapFromERC20{} _ sdk.Msg = &MsgSwapToERC20{} + + + regexpERC20Fmt = fmt.Sprintf("^[a-z][a-z0-9/]{%d,%d}$", tokentypes.MinimumSymbolLen-1, tokentypes.MaximumSymbolLen-1) + regexpERC20 = regexp.MustCompile(regexpERC20Fmt).MatchString ) // NewMsgIssueToken - construct token issue msg. @@ -358,8 +365,22 @@ func (m *MsgUpdateParams) GetSigners() []sdk.AccAddress { // ValidateBasic implements Msg func (m *MsgDeployERC20) ValidateBasic() error { - // TODO - return nil + if _, err := sdk.AccAddressFromBech32(m.Authority); err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid authority address (%s)", err) + } + + if err := tokentypes.ValidateName(m.Name); err != nil { + return err + } + + if err := tokentypes.ValidateScale(m.Scale); err != nil { + return err + } + + if err := ValidateERC20(m.MinUnit); err != nil { + return err + } + return ValidateERC20(m.Symbol) } // GetSigners returns the expected signers for a MsgDeployERC20 message @@ -392,3 +413,20 @@ func (m *MsgSwapToERC20) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{addr} } + + +// ValidateERC20 validates ERC20 symbol or name +func ValidateERC20(params string) error { + if !regexpERC20(params) { + return errorsmod.Wrapf( + tokentypes.ErrInvalidSymbol, + "invalid symbol or name: %s, only accepts english lowercase letters, numbers or slash, length [%d, %d], and begin with an english letter, regexp: %s", + params, + tokentypes.MinimumSymbolLen, + tokentypes.MaximumSymbolLen, + regexpERC20Fmt, + ) + } + return nil +} + From 58339127426ced827d014768da6c3ca0d7711cc8 Mon Sep 17 00:00:00 2001 From: dreamer Date: Sun, 14 Apr 2024 10:31:05 +0800 Subject: [PATCH 2/5] fix test error --- modules/token/depinject.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/token/depinject.go b/modules/token/depinject.go index 19ee134b..772a427f 100644 --- a/modules/token/depinject.go +++ b/modules/token/depinject.go @@ -45,8 +45,10 @@ type TokenInputs struct { AccountKeeper types.AccountKeeper BankKeeper types.BankKeeper - EVMKeeper types.EVMKeeper - ICS20Keeper types.ICS20Keeper + + // TODO: EVMKeeper and ICS20Keeper must be injected in the production environment, where `optional:"true"` is only used for testing. + EVMKeeper types.EVMKeeper `optional:"true"` + ICS20Keeper types.ICS20Keeper `optional:"true"` // LegacySubspace is used solely for migration of x/params managed parameters LegacySubspace exported.Subspace `optional:"true"` From 359c74c85caeb639a955e8b5f0c6e8b1a2ebac72 Mon Sep 17 00:00:00 2001 From: dreamer Date: Sun, 14 Apr 2024 17:49:13 +0800 Subject: [PATCH 3/5] refactor code --- modules/token/depinject.go | 11 +++-- modules/token/keeper/erc20.go | 32 ++++++++++++- modules/token/keeper/msg_server.go | 50 +++++-------------- modules/token/types/expected_keepers.go | 1 + simapp/app_v2.go | 6 +++ simapp/mocks/depinject.go | 25 ++++++++++ simapp/mocks/evm.go | 64 +++++++++++++++++++++++++ 7 files changed, 146 insertions(+), 43 deletions(-) create mode 100644 simapp/mocks/depinject.go create mode 100644 simapp/mocks/evm.go diff --git a/modules/token/depinject.go b/modules/token/depinject.go index 772a427f..7337668c 100644 --- a/modules/token/depinject.go +++ b/modules/token/depinject.go @@ -36,6 +36,7 @@ func (am AppModule) IsOnePerModuleType() {} // IsAppModule implements the appmodule.AppModule interface. func (am AppModule) IsAppModule() {} +// TokenInputs is the input of the Token module type TokenInputs struct { depinject.In @@ -45,15 +46,14 @@ type TokenInputs struct { AccountKeeper types.AccountKeeper BankKeeper types.BankKeeper - - // TODO: EVMKeeper and ICS20Keeper must be injected in the production environment, where `optional:"true"` is only used for testing. - EVMKeeper types.EVMKeeper `optional:"true"` - ICS20Keeper types.ICS20Keeper `optional:"true"` + EVMKeeper types.EVMKeeper + ICS20Keeper types.ICS20Keeper // LegacySubspace is used solely for migration of x/params managed parameters LegacySubspace exported.Subspace `optional:"true"` } +// TokenOutputs is the output of the Token module type TokenOutputs struct { depinject.Out @@ -62,6 +62,9 @@ type TokenOutputs struct { } +// ProvideModule provides a module for the token with the given inputs and returns the token keeper and module. +// +// Takes TokenInputs as input parameters and returns TokenOutputs. func ProvideModule(in TokenInputs) TokenOutputs { // default to governance authority if not provided authority := authtypes.NewModuleAddress(govtypes.ModuleName) diff --git a/modules/token/keeper/erc20.go b/modules/token/keeper/erc20.go index 13865358..fc1e9337 100644 --- a/modules/token/keeper/erc20.go +++ b/modules/token/keeper/erc20.go @@ -3,6 +3,7 @@ package keeper import ( errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -68,8 +69,35 @@ func (k Keeper) DeployERC20( return contractAddr, nil } - func (k Keeper) moduleAddress() common.Address { moduleAddr := k.accountKeeper.GetModuleAddress(types.ModuleName) return common.BytesToAddress(moduleAddr.Bytes()) -} \ No newline at end of file +} + +func (k Keeper) buildERC20Token( + ctx sdk.Context, + name string, + symbol string, + minUnit string, + scale uint32, +) (*v1.Token, error) { + if !k.HasMinUint(ctx, minUnit) { + if !k.ics20Keeper.HasTrace(ctx, minUnit) { + return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "token: %s does not exist", minUnit) + } + return &v1.Token{ + Symbol: symbol, + Name: name, + Scale: scale, + MinUnit: minUnit, + Mintable: true, + Owner: k.accountKeeper.GetModuleAddress(types.ModuleName).String(), + }, nil + } + + token, err := k.getTokenByMinUnit(ctx, minUnit) + if err != nil { + return nil, err + } + return &token, nil +} diff --git a/modules/token/keeper/msg_server.go b/modules/token/keeper/msg_server.go index 5609b1dc..767a107e 100644 --- a/modules/token/keeper/msg_server.go +++ b/modules/token/keeper/msg_server.go @@ -272,48 +272,24 @@ func (m msgServer) UpdateParams( // DeployERC20 implements v1.MsgServer. func (m msgServer) DeployERC20(goCtx context.Context, msg *v1.MsgDeployERC20) (*v1.MsgDeployERC20Response, error) { - var ( - ctx = sdk.UnwrapSDKContext(goCtx) - name = msg.Name - symbol = msg.Symbol - scale = msg.Scale - minUnit = msg.MinUnit - token v1.Token - err error - ) - - if !m.k.HasMinUint(ctx, msg.MinUnit) { - if !m.k.ics20Keeper.HasTrace(ctx, msg.MinUnit) { - return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "token: %s not exist", msg.MinUnit) - } - token = v1.Token{ - Symbol: symbol, - Name: name, - Scale: scale, - MinUnit: msg.MinUnit, - Mintable: true, - Owner: m.k.accountKeeper.GetModuleAddress(types.ModuleName).String(), - } - } else { - token, err = m.k.getTokenByMinUnit(ctx, msg.MinUnit) - if err != nil { - return nil, err - } - if len(token.Contract) > 0 { - return nil, errorsmod.Wrapf(types.ErrERC20AlreadyExists, "token: %s already deployed erc20 contract: %s", token.Symbol, token.Contract) - } - name = token.Name - symbol = token.Symbol - scale = token.Scale - minUnit = token.MinUnit + ctx := sdk.UnwrapSDKContext(goCtx) + + token, err := m.k.buildERC20Token(ctx, msg.Name, msg.Symbol, msg.MinUnit, msg.Scale) + if err != nil { + return nil, err } - contractAddr, err := m.k.DeployERC20(ctx, name, symbol, minUnit, int8(scale)) + if len(token.Contract) > 0 { + return nil, errorsmod.Wrapf(types.ErrERC20AlreadyExists, "token: %s already deployed erc20 contract: %s", token.Symbol, token.Contract) + } + + contractAddr, err := m.k.DeployERC20(ctx, token.Name, token.Symbol, token.MinUnit, int8(token.Scale)) if err != nil { return nil, err } + token.Contract = contractAddr.String() - m.k.upsertToken(ctx, token) + m.k.upsertToken(ctx, *token) return &v1.MsgDeployERC20Response{}, nil } @@ -325,4 +301,4 @@ func (m msgServer) SwapFromERC20(context.Context, *v1.MsgSwapFromERC20) (*v1.Msg // SwapToERC20 implements v1.MsgServer. func (m msgServer) SwapToERC20(context.Context, *v1.MsgSwapToERC20) (*v1.MsgSwapToERC20Response, error) { panic("unimplemented") -} \ No newline at end of file +} diff --git a/modules/token/types/expected_keepers.go b/modules/token/types/expected_keepers.go index 84312491..5a766176 100644 --- a/modules/token/types/expected_keepers.go +++ b/modules/token/types/expected_keepers.go @@ -69,3 +69,4 @@ type EVMKeeper interface { type ICS20Keeper interface{ HasTrace(ctx sdk.Context, denom string) bool } + diff --git a/simapp/app_v2.go b/simapp/app_v2.go index 6d96b362..6ee3e471 100644 --- a/simapp/app_v2.go +++ b/simapp/app_v2.go @@ -104,6 +104,8 @@ import ( "github.com/irisnet/irismod/modules/token" tokenkeeper "github.com/irisnet/irismod/modules/token/keeper" tokentypes "github.com/irisnet/irismod/modules/token/types" + + "github.com/irisnet/irismod/simapp/mocks" ) var ( @@ -277,6 +279,10 @@ func NewSimApp( // For providing a custom inflation function for x/mint add here your // custom function that implements the minttypes.InflationCalculationFn // interface. + + // For providing a mock evm function for token module + mocks.ProvideEVMKeeper(), + mocks.ProvideICS20Keeper(), ), ) ) diff --git a/simapp/mocks/depinject.go b/simapp/mocks/depinject.go new file mode 100644 index 00000000..5abe9fb4 --- /dev/null +++ b/simapp/mocks/depinject.go @@ -0,0 +1,25 @@ +package mocks + +import ( + "github.com/ethereum/go-ethereum/common" + + tokentypes "github.com/irisnet/irismod/modules/token/types" +) + +// ProvideEVMKeeper returns an instance of tokentypes.EVMKeeper. +// +// No parameters. +// Returns a tokentypes.EVMKeeper. +func ProvideEVMKeeper() tokentypes.EVMKeeper { + return &evm{ + erc20s: make(map[common.Address]*erc20), + } +} + +// ProvideICS20Keeper returns an instance of tokentypes.ICS20Keeper. +// +// No parameters. +// Returns a tokentypes.ICS20Keeper. +func ProvideICS20Keeper() tokentypes.ICS20Keeper { + return &transferKeeper{} +} \ No newline at end of file diff --git a/simapp/mocks/evm.go b/simapp/mocks/evm.go new file mode 100644 index 00000000..0cc36668 --- /dev/null +++ b/simapp/mocks/evm.go @@ -0,0 +1,64 @@ +package mocks + +import ( + "context" + "math/big" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/vm" + + tokentypes "github.com/irisnet/irismod/modules/token/types" + "github.com/irisnet/irismod/types" +) + +var ( + _ tokentypes.EVMKeeper = (*evm)(nil) + _ tokentypes.ICS20Keeper = (*transferKeeper)(nil) +) + +type evm struct { + erc20s map[common.Address]*erc20 +} + +// ApplyMessage implements types.EVMKeeper. +func (e *evm) ApplyMessage(ctx sdk.Context, msg core.Message, tracer vm.EVMLogger, commit bool) (*types.Result, error) { + panic("unimplemented") +} + +// ChainID implements types.EVMKeeper. +func (e *evm) ChainID() *big.Int { + return big.NewInt(16688) +} + +// EstimateGas implements types.EVMKeeper. +func (e *evm) EstimateGas(ctx context.Context, req *types.EthCallRequest) (uint64, error) { + return 3000000, nil +} + +// FeeDenom implements types.EVMKeeper. +func (e *evm) FeeDenom() string { + return "eris" +} + +// SupportedKey implements types.EVMKeeper. +func (e *evm) SupportedKey(pubKey cryptotypes.PubKey) bool { + return true +} + +type erc20 struct { + scale int8 + name, symbol string + + balance map[common.Address]*big.Int +} + +type transferKeeper struct{} + +// HasTrace implements types.ICS20Keeper. +func (t *transferKeeper) HasTrace(ctx sdk.Context, denom string) bool { + return true +} From 82c18620c64c6127b9ed8472709dcbb3e9dd28bc Mon Sep 17 00:00:00 2001 From: dreamer Date: Mon, 15 Apr 2024 10:22:35 +0800 Subject: [PATCH 4/5] add Authority check --- modules/token/keeper/msg_server.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/token/keeper/msg_server.go b/modules/token/keeper/msg_server.go index 767a107e..d054b2ab 100644 --- a/modules/token/keeper/msg_server.go +++ b/modules/token/keeper/msg_server.go @@ -272,8 +272,16 @@ func (m msgServer) UpdateParams( // DeployERC20 implements v1.MsgServer. func (m msgServer) DeployERC20(goCtx context.Context, msg *v1.MsgDeployERC20) (*v1.MsgDeployERC20Response, error) { - ctx := sdk.UnwrapSDKContext(goCtx) + if m.k.authority != msg.Authority { + return nil, errorsmod.Wrapf( + sdkerrors.ErrUnauthorized, + "invalid authority; expected %s, got %s", + m.k.authority, + msg.Authority, + ) + } + ctx := sdk.UnwrapSDKContext(goCtx) token, err := m.k.buildERC20Token(ctx, msg.Name, msg.Symbol, msg.MinUnit, msg.Scale) if err != nil { return nil, err From f11ef390d5650f708e05c1ebd3bad8c56a0eadaf Mon Sep 17 00:00:00 2001 From: dreamer Date: Mon, 15 Apr 2024 13:08:45 +0800 Subject: [PATCH 5/5] add exist check for symbol --- modules/token/keeper/erc20.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/token/keeper/erc20.go b/modules/token/keeper/erc20.go index fc1e9337..5ed53dab 100644 --- a/modules/token/keeper/erc20.go +++ b/modules/token/keeper/erc20.go @@ -82,6 +82,9 @@ func (k Keeper) buildERC20Token( scale uint32, ) (*v1.Token, error) { if !k.HasMinUint(ctx, minUnit) { + if k.HasSymbol(ctx, symbol) { + return nil, errorsmod.Wrapf(types.ErrSymbolAlreadyExists, "symbol already exists: %s", symbol) + } if !k.ics20Keeper.HasTrace(ctx, minUnit) { return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "token: %s does not exist", minUnit) }