From 1d7a58df99a3641ca1dd93b69a2a4bea66bd59ab Mon Sep 17 00:00:00 2001 From: Peter Kieltyka Date: Fri, 27 Sep 2024 08:56:12 -0400 Subject: [PATCH] intents: add new contractCall transaction type (#157) --- go.mod | 12 +- go.sum | 16 +- go.work.sum | 6 + intents/intent.gen.go | 19 +- intents/intent.ridl | 18 +- .../intent_data_transaction_contract_abi.go | 144 ++++++++ ...tent_data_transaction_contract_abi_test.go | 162 ++++++++ .../intent_data_transaction_delayed_abi.go | 83 +---- ...ntent_data_transaction_delayed_abi_test.go | 96 +---- intents/intent_data_transaction_ext.go | 43 ++- intents/intent_data_transaction_ext_test.go | 348 ++++++++++++++++-- utils.go | 1 - 12 files changed, 742 insertions(+), 206 deletions(-) create mode 100644 intents/intent_data_transaction_contract_abi.go create mode 100644 intents/intent_data_transaction_contract_abi_test.go diff --git a/go.mod b/go.mod index ba16fe15..66381ea6 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module github.com/0xsequence/go-sequence -go 1.22 +go 1.22.0 -toolchain go1.22.6 +toolchain go1.23.1 // replace github.com/0xsequence/ethkit => /Users/peter/Dev/0xsequence/ethkit @@ -12,7 +12,7 @@ require ( github.com/BurntSushi/toml v1.2.1 github.com/davecgh/go-spew v1.1.1 github.com/gibson042/canonicaljson-go v1.0.3 - github.com/goware/cachestore v0.8.1 + github.com/goware/cachestore v0.9.0 github.com/goware/logger v0.3.0 github.com/shopspring/decimal v1.4.0 github.com/stretchr/testify v1.9.0 @@ -26,7 +26,7 @@ require ( github.com/btcsuite/btcd/btcutil v1.1.6 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/consensys/bavard v0.1.15 // indirect + github.com/consensys/bavard v0.1.17 // indirect github.com/consensys/gnark-crypto v0.14.0 // indirect github.com/crate-crypto/go-kzg-4844 v1.1.0 // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect @@ -49,8 +49,8 @@ require ( github.com/supranational/blst v0.3.13 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect golang.org/x/crypto v0.27.0 // indirect - golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect - golang.org/x/net v0.28.0 // indirect + golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect + golang.org/x/net v0.29.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.25.0 // indirect golang.org/x/text v0.18.0 // indirect diff --git a/go.sum b/go.sum index 44c4afb1..b946e8c8 100644 --- a/go.sum +++ b/go.sum @@ -44,8 +44,8 @@ github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/consensys/bavard v0.1.15 h1:fxv2mg1afRMJvZgpwEgLmyr2MsQwaAYcyKf31UBHzw4= -github.com/consensys/bavard v0.1.15/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/bavard v0.1.17 h1:53CdY/g35YSH9oRoa/b29tZinaiOEJYBmf9vydozPpE= +github.com/consensys/bavard v0.1.17/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark-crypto v0.14.0 h1:DDBdl4HaBtdQsq/wfMwJvZNE80sHidrK3Nfrefatm0E= github.com/consensys/gnark-crypto v0.14.0/go.mod h1:CU4UijNPsHawiVGNxe9co07FkzCeWHHrb1li/n1XoU0= github.com/crate-crypto/go-kzg-4844 v1.1.0 h1:EN/u9k2TF6OWSHrCCDBBU6GLNMq88OspHHlMnHfoyU4= @@ -92,8 +92,8 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/goware/breaker v0.1.2 h1:er7Jo7OAUdKyN0iXBQGI2x18MiXjTKNaE4P+ceimNzE= github.com/goware/breaker v0.1.2/go.mod h1:ijCEfXAa0j6w7IoHA4v6Sox2W6U9HUbI/t+5x0zGaug= -github.com/goware/cachestore v0.8.1 h1:UjSjFiB27vjGq4JK8aBMqsAI4sa7cbrHToFDsM0cjHw= -github.com/goware/cachestore v0.8.1/go.mod h1:ikiO2RmxIt4cVqEBII6yR+V4Z7pH+y8bMQHpd1MvG1Y= +github.com/goware/cachestore v0.9.0 h1:gx0j0DZpg5Nwzkm5DjRXB9VuHXWKX+Mqw0IhkCzET9Q= +github.com/goware/cachestore v0.9.0/go.mod h1:ikiO2RmxIt4cVqEBII6yR+V4Z7pH+y8bMQHpd1MvG1Y= github.com/goware/calc v0.2.0 h1:3B9qjXYpE0kgS4LhyklbM6X/0cOvZLdUZG7sdAuVCb4= github.com/goware/calc v0.2.0/go.mod h1:BSQUbfS6ICW9RvSV9SikDY+t6/HQKI+CUxIpjE3VD28= github.com/goware/channel v0.4.1 h1:N6AqSuB6ZMOrfezhpQJ2xo5Y6jlJES+m+P+JyyX9XIo= @@ -163,15 +163,15 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= -golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= diff --git a/go.work.sum b/go.work.sum index 65d91723..48ed467a 100644 --- a/go.work.sum +++ b/go.work.sum @@ -54,6 +54,7 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= +github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -66,6 +67,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -117,6 +119,7 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= +github.com/goware/pp v0.0.3/go.mod h1:shID9y83CUGdg/BfO0SrVhchPpIAcT3ArfLVkq3x7tQ= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= @@ -170,6 +173,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -180,6 +184,7 @@ github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -480,6 +485,7 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/intents/intent.gen.go b/intents/intent.gen.go index 18af93c4..0ea952e5 100644 --- a/intents/intent.gen.go +++ b/intents/intent.gen.go @@ -1,4 +1,4 @@ -// sequence-waas-intents v0.1.0 1fe0a24abef81231c54c0886157c65ef738d5ed6 +// sequence-waas-intents v0.1.0 2e4f5d4a4107d8e8c74c252f4d1a7aad391db6e7 // -- // Code generated by webrpc-gen@v0.19.3 with golang generator. DO NOT EDIT. // @@ -25,7 +25,7 @@ func WebRPCSchemaVersion() string { // Schema hash generated from your RIDL schema func WebRPCSchemaHash() string { - return "1fe0a24abef81231c54c0886157c65ef738d5ed6" + return "2e4f5d4a4107d8e8c74c252f4d1a7aad391db6e7" } // @@ -82,6 +82,7 @@ const ( TransactionType_erc721send TransactionType = "erc721send" TransactionType_erc1155send TransactionType = "erc1155send" TransactionType_delayedEncode TransactionType = "delayedEncode" + TransactionType_contractCall TransactionType = "contractCall" ) func (x TransactionType) MarshalText() ([]byte, error) { @@ -349,6 +350,12 @@ type TransactionRaw struct { Data string `json:"data"` } +type AbiData struct { + Abi string `json:"abi"` + Func *string `json:"func"` + Args []interface{} `json:"args"` +} + type TransactionERC20 struct { Type string `json:"type"` TokenAddress string `json:"tokenAddress"` @@ -370,6 +377,7 @@ type TransactionERC1155Value struct { Amount string `json:"amount"` } +// Deprecated: TransactionDelayedEncode is not type safe, please use TransactionContractCall instead type TransactionDelayedEncode struct { Type string `json:"type"` To string `json:"to"` @@ -377,6 +385,13 @@ type TransactionDelayedEncode struct { Data json.RawMessage `json:"data"` } +type TransactionContractCall struct { + Type string `json:"type"` + To string `json:"to"` + Value string `json:"value"` + Data AbiData `json:"data"` +} + type TransactionERC1155 struct { Type string `json:"type"` TokenAddress string `json:"tokenAddress"` diff --git a/intents/intent.ridl b/intents/intent.ridl index de41759d..007f07eb 100644 --- a/intents/intent.ridl +++ b/intents/intent.ridl @@ -151,15 +151,22 @@ struct IntentDataGetIdToken struct TransactionRaw - type: string - to: string - - value: string + - value?: string + + go.field.type = string - data: string +struct AbiData + - abi: string + - func?: string + - args: []any + enum TransactionType: string - transaction - erc20send - erc721send - erc1155send - delayedEncode + - contractCall struct TransactionERC20 - type: string @@ -180,6 +187,7 @@ struct TransactionERC1155Value + go.field.name = ID - amount: string +# Deprecated: TransactionDelayedEncode is not type safe, please use TransactionContractCall instead struct TransactionDelayedEncode - type: string - to: string @@ -187,6 +195,14 @@ struct TransactionDelayedEncode - data: any + go.field.type = json.RawMessage +struct TransactionContractCall + - type: string + - to: string + - value?: string + + go.field.type = string + - data: AbiData + + go.field.type = AbiData + struct TransactionERC1155 - type: string - tokenAddress: string diff --git a/intents/intent_data_transaction_contract_abi.go b/intents/intent_data_transaction_contract_abi.go new file mode 100644 index 00000000..7c5bd677 --- /dev/null +++ b/intents/intent_data_transaction_contract_abi.go @@ -0,0 +1,144 @@ +package intents + +import ( + "encoding/json" + "errors" + "fmt" + "strings" + + "github.com/0xsequence/ethkit/ethcoder" + "github.com/0xsequence/ethkit/go-ethereum/common" +) + +type contractCallType struct { + Abi string `json:"abi"` + Func string `json:"func"` + Args []any `json:"args"` +} + +func EncodeContractCall(data *contractCallType) (string, error) { + // Get the method from the abi + method, _, err := getMethodFromAbi(data.Abi, data.Func) + if err != nil { + return "", err + } + + enc := make([]string, len(data.Args)) + + // String args can be used right away, but any nested + // `contractCallType` must be handled recursively + for i, arg := range data.Args { + switch arg := arg.(type) { + case string: + enc[i] = arg + + case map[string]interface{}: + nst := arg + + var funcName string + if v, ok := nst["func"].(string); ok { + funcName = v + } + + args, ok := nst["args"].([]interface{}) + if !ok { + return "", fmt.Errorf("nested args expected to be an array") + } + + abi, _ := nst["abi"].(string) + + enc[i], err = EncodeContractCall(&contractCallType{ + Abi: abi, + Func: funcName, + Args: args, + }) + if err != nil { + return "", err + } + + default: + return "", fmt.Errorf("invalid arg type") + } + } + + // Encode the method call + res, err := ethcoder.AbiEncodeMethodCalldataFromStringValues(method, enc) + if err != nil { + return "", err + } + + return "0x" + common.Bytes2Hex(res), nil +} + +// The abi may be a: +// - already encoded method abi: transferFrom(address,address,uint256) +// - already encoded named method: transferFrom(address from,address to,uint256 val) +// - an array of function abis: "[{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_orderId\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"_maxCost\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_fees\",\"type\":\"address\"}],\"name\":\"fillOrKillOrder\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"}]" +// - or a single function abi: "{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_orderId\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"_maxCost\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_fees\",\"type\":\"address\"}],\"name\":\"fillOrKillOrder\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"}" +// And it must always return it encoded, like this: +// - transferFrom(address,address,uint256) +// making sure that the method matches the returned one +func getMethodFromAbi(abi string, method string) (string, []string, error) { + // + // First attempt to parse `abi` string as a plain method abi + // ie. transferFrom(address,address,uint256) + // + + // Handle the case for already encoded method abi. + // NOTE: we do not need the know the `method` argument here. + abi = strings.TrimSpace(abi) + if len(abi) > 0 && strings.Contains(abi, "(") && abi[len(abi)-1] == ')' { + // NOTE: even though the ethcoder function is `ParseEventDef`, designed for event type parsing + // the abi format for a single function structure is the same, so it works. Perhaps we will rename + // `ParseEventDef` in the future, or just add another method with a different name. + eventDef, err := ethcoder.ParseEventDef(abi) + if err != nil { + return "", nil, err + } + return eventDef.Sig, eventDef.ArgNames, nil + } + + // + // If above didn't work, attempt to parse `abi` string as + // a JSON object of the full abi definition + // + + type FunctionAbi struct { + Name string `json:"name"` + Type string `json:"type"` + Inputs []struct { + InternalType string `json:"internalType"` + Name string `json:"name"` + Type string `json:"type"` + } `json:"inputs"` + } + + // Handle array of function abis and single function abi + var abis []FunctionAbi + if strings.HasPrefix(abi, "[") { + if err := json.Unmarshal([]byte(abi), &abis); err != nil { + return "", nil, err + } + } else { + var singleAbi FunctionAbi + if err := json.Unmarshal([]byte(abi), &singleAbi); err != nil { + return "", nil, err + } + abis = append(abis, singleAbi) + } + + // Find the correct method and encode it + for _, fnAbi := range abis { + if fnAbi.Name == method { + var paramTypes []string + order := make([]string, len(fnAbi.Inputs)) + for i, input := range fnAbi.Inputs { + paramTypes = append(paramTypes, input.Type) + order[i] = input.Name + } + return method + "(" + strings.Join(paramTypes, ",") + ")", order, nil + } + } + + return "", nil, errors.New("Method not found in ABI") +} diff --git a/intents/intent_data_transaction_contract_abi_test.go b/intents/intent_data_transaction_contract_abi_test.go new file mode 100644 index 00000000..b6426f0d --- /dev/null +++ b/intents/intent_data_transaction_contract_abi_test.go @@ -0,0 +1,162 @@ +package intents + +import ( + "testing" + + "github.com/0xsequence/ethkit/ethcoder" + "github.com/0xsequence/ethkit/go-ethereum/common" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetMethodFromABI(t *testing.T) { + // From ABI, alone, in array + res, order, err := getMethodFromAbi(`[{"name":"transfer","type":"function","inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}]}]`, "transfer") + assert.Nil(t, err) + + assert.Equal(t, "transfer(address,uint256)", res) + assert.Equal(t, []string{"_to", "_value"}, order) + + // From ABI, alone, as object + res, order, err = getMethodFromAbi(`{"name":"transfer","type":"function","inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}]}`, "transfer") + assert.Nil(t, err) + + assert.Equal(t, "transfer(address,uint256)", res) + assert.Equal(t, []string{"_to", "_value"}, order) + + // From ABI, with many args + res, order, err = getMethodFromAbi(`[{"inputs":[{"internalType":"bytes32","name":"_orderId","type":"bytes32"},{"internalType":"uint256","name":"_maxCost","type":"uint256"},{"internalType":"address[]","name":"_fees","type":"address[]"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"fillOrKillOrder","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_val","type":"uint256"},{"internalType":"string","name":"_data","type":"string"}],"name":"notExpired","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"otherMethods","outputs":[],"stateMutability":"nonpayable","type":"function"}]`, "fillOrKillOrder") + assert.Nil(t, err) + + assert.Equal(t, "fillOrKillOrder(bytes32,uint256,address[],bytes)", res) + assert.Equal(t, []string{"_orderId", "_maxCost", "_fees", "_data"}, order) + + res, order, err = getMethodFromAbi(`[{"inputs":[{"internalType":"bytes32","name":"_orderId","type":"bytes32"},{"internalType":"uint256","name":"_maxCost","type":"uint256"},{"internalType":"address[]","name":"_fees","type":"address[]"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"fillOrKillOrder","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_val","type":"uint256"},{"internalType":"string","name":"_data","type":"string"}],"name":"notExpired","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"otherMethods","outputs":[],"stateMutability":"nonpayable","type":"function"}]`, "notExpired") + assert.Nil(t, err) + + assert.Equal(t, "notExpired(uint256,string)", res) + assert.Equal(t, []string{"_val", "_data"}, order) + + res, order, err = getMethodFromAbi(`[{"inputs":[{"internalType":"bytes32","name":"_orderId","type":"bytes32"},{"internalType":"uint256","name":"_maxCost","type":"uint256"},{"internalType":"address[]","name":"_fees","type":"address[]"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"fillOrKillOrder","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_val","type":"uint256"},{"internalType":"string","name":"_data","type":"string"}],"name":"notExpired","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"otherMethods","outputs":[],"stateMutability":"nonpayable","type":"function"}]`, "otherMethods") + assert.Nil(t, err) + + assert.Equal(t, "otherMethods()", res) + assert.Equal(t, []string{}, order) + + // From plain method, without named args + res, order, err = getMethodFromAbi(`transfer(address,uint256)`, "transfer") + assert.Nil(t, err) + + assert.Equal(t, "transfer(address,uint256)", res) + assert.Equal(t, []string{"arg1", "arg2"}, order) + + // From plain method, with named args + res, order, err = getMethodFromAbi(`transfer(address _to,uint256 _value, bytes _mas)`, "transfer") + assert.Nil(t, err) + + assert.Equal(t, "transfer(address,uint256,bytes)", res) + assert.Equal(t, []string{"_to", "_value", "_mas"}, order) + + // Mixed plain method should return nil order + res, order, err = getMethodFromAbi(`transfer(address _to,uint256, bytes _mas)`, "transfer") + + assert.Nil(t, err) + assert.Equal(t, "transfer(address,uint256,bytes)", res) + assert.Equal(t, []string{"_to", "arg2", "_mas"}, order) +} + +func TestEncodeContractCall(t *testing.T) { + // Encode simple transferFrom, not named + res, err := EncodeContractCall(&contractCallType{ + Abi: `[{"name":"transferFrom","type":"function","inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}]}]`, + Func: "transferFrom", + Args: []any{"0x0dc9603d4da53841C1C83f3B550C6143e60e0425", "0x0dc9603d4da53841C1C83f3B550C6143e60e0425", "100"}, + }) + require.Nil(t, err) + require.Equal(t, "0x23b872dd0000000000000000000000000dc9603d4da53841c1c83f3b550c6143e60e04250000000000000000000000000dc9603d4da53841c1c83f3b550c6143e60e04250000000000000000000000000000000000000000000000000000000000000064", res) + + // Encode simple transferFrom, selector + res, err = EncodeContractCall(&contractCallType{ + Abi: `transferFrom(address,address,uint256)`, + Args: []any{"0x0dc9603d4da53841C1C83f3B550C6143e60e0425", "0x0dc9603d4da53841C1C83f3B550C6143e60e0425", "100"}, + }) + require.Nil(t, err) + require.Equal(t, "0x23b872dd0000000000000000000000000dc9603d4da53841c1c83f3b550c6143e60e04250000000000000000000000000dc9603d4da53841c1c83f3b550c6143e60e04250000000000000000000000000000000000000000000000000000000000000064", res) + + // Encode simple transferFrom, named + // res, err = EncodeContractCall(&contractCallType{ + // Abi: `[{"name":"transferFrom","type":"function","inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}]}]`, + // Func: "transferFrom", + // Args: json.RawMessage(`{"_from": "0x0dc9603d4da53841C1C83f3B550C6143e60e0425", "_value": "100", "_to": "0x0dc9603d4da53841C1C83f3B550C6143e60e0425"}`), + // }) + // require.Nil(t, err) + // require.Equal(t, res, "0x23b872dd0000000000000000000000000dc9603d4da53841c1c83f3b550c6143e60e04250000000000000000000000000dc9603d4da53841c1c83f3b550c6143e60e04250000000000000000000000000000000000000000000000000000000000000064") + + // Encode simple transferFrom, not named, passed as function + res, err = EncodeContractCall(&contractCallType{ + Abi: `transferFrom(address,address,uint256)`, + Args: []any{"0x13915b1ea28Fd2E8197c88ff9D2422182E83bf25", "0x4Ad47F1611c78C824Ff3892c4aE1CC04637D6462", "5192381927398174182391237"}, + }) + require.Nil(t, err) + require.Equal(t, "0x23b872dd00000000000000000000000013915b1ea28fd2e8197c88ff9d2422182e83bf250000000000000000000000004ad47f1611c78c824ff3892c4ae1cc04637d6462000000000000000000000000000000000000000000044b87969b06250e50bdc5", res) + + // Encode simple transferFrom, named, passed as function + // res, err = EncodeContractCall(&contractCallType{ + // Abi: `transferFrom(address _from,address _to,uint256 _value)`, + // Func: "transferFrom", + // Args: json.RawMessage(`{"_from": "0x13915b1ea28Fd2E8197c88ff9D2422182E83bf25", "_value": "5192381927398174182391237", "_to": "0x4Ad47F1611c78C824Ff3892c4aE1CC04637D6462"}`), + // }) + // require.Nil(t, err) + // require.Equal(t, res, "0x23b872dd00000000000000000000000013915b1ea28fd2e8197c88ff9d2422182e83bf250000000000000000000000004ad47f1611c78c824ff3892c4ae1cc04637d6462000000000000000000000000000000000000000000044b87969b06250e50bdc5") + + // // Encode nested bytes, passed as function + // nestedEncodeType1 := &contractCallType{ + // Abi: `transferFrom(uint256)`, + // Args: []any{"481923749816926378123"}, + // } + + // nestedEncodeType2 := &contractCallType{ + // Abi: `hola(string)`, + // Args: []any{"mundo"}, + // } + + // net1jsn, err := json.Marshal(nestedEncodeType1) + // require.Nil(t, err) + + // net2jsn, err := json.Marshal(nestedEncodeType2) + // require.Nil(t, err) + + arg2, err := ethcoder.AbiEncodeMethodCalldataFromStringValues("transferFrom(uint256)", []string{"481923749816926378123"}) + require.NoError(t, err) + + arg3, err := ethcoder.AbiEncodeMethodCalldataFromStringValues("hola(string)", []string{"mundo"}) + require.NoError(t, err) + + arg2Hex := "0x" + common.Bytes2Hex(arg2) + arg3Hex := "0x" + common.Bytes2Hex(arg3) + + res, err = EncodeContractCall(&contractCallType{ + Abi: "caller(address,bytes,bytes)", + Func: "caller", + Args: []any{"0x13915b1ea28Fd2E8197c88ff9D2422182E83bf25", arg2Hex, arg3Hex}, + }) + require.Nil(t, err) + require.Equal(t, "0x8b6701df00000000000000000000000013915b1ea28fd2e8197c88ff9d2422182e83bf25000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002477a11f7e00000000000000000000000000000000000000000000001a2009191df61e988b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000646ce8ea55000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000056d756e646f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", res) + + // Fail passing named args to non-named abi + // res, err = EncodeContractCall(&contractCallType{ + // Abi: `transferFrom(address,uint256)`, + // Func: "transferFrom", + // Args: json.RawMessage(`{"_from": "0x13915b1ea28Fd2E8197c88ff9D2422182E83bf25", "_value": "5192381927398174182391237", "_to": "0x4Ad47F1611c78C824Ff3892c4aE1CC04637D6462"}`), + // }) + // assert.NotNil(t, err) + + // Accept passing ordened args to named abi + res, err = EncodeContractCall(&contractCallType{ + Abi: `transferFrom(address _from,address _to,uint256 _value)`, + Func: "transferFrom", + Args: []any{"0x13915b1ea28Fd2E8197c88ff9D2422182E83bf25", "0x4Ad47F1611c78C824Ff3892c4aE1CC04637D6462", "9"}, + }) + require.Nil(t, err) + require.Equal(t, "0x23b872dd00000000000000000000000013915b1ea28fd2e8197c88ff9d2422182e83bf250000000000000000000000004ad47f1611c78c824ff3892c4ae1cc04637d64620000000000000000000000000000000000000000000000000000000000000009", res) +} diff --git a/intents/intent_data_transaction_delayed_abi.go b/intents/intent_data_transaction_delayed_abi.go index 52103e76..56fec2a1 100644 --- a/intents/intent_data_transaction_delayed_abi.go +++ b/intents/intent_data_transaction_delayed_abi.go @@ -2,20 +2,20 @@ package intents import ( "encoding/json" - "errors" "fmt" - "strings" "github.com/0xsequence/ethkit/ethcoder" "github.com/0xsequence/ethkit/go-ethereum/common" ) +// Deprecated: use EncodeContractCall instead type delayedEncodeType struct { Abi string `json:"abi"` Func string `json:"func"` Args json.RawMessage `json:"args"` } +// Deprecated: use EncodeContractCall instead func EncodeDelayedABI(data *delayedEncodeType) (string, error) { // Get the method from the abi method, order, err := getMethodFromAbi(data.Abi, data.Func) @@ -31,12 +31,12 @@ func EncodeDelayedABI(data *delayedEncodeType) (string, error) { // String args can be used right away, but any nested // `delayedEncodeType` must be handled recursively for i, arg := range args1 { - switch arg.(type) { + switch arg := arg.(type) { case string: - enc[i] = arg.(string) + enc[i] = arg case map[string]interface{}: - nst := arg.(map[string]interface{}) + nst := arg rjsn, err := json.Marshal(nst["args"]) if err != nil { @@ -96,76 +96,3 @@ func EncodeDelayedABI(data *delayedEncodeType) (string, error) { return "", err } - -// The abi may be a: -// - already encoded method abi: transferFrom(address,address,uint256) -// - already encoded named method: transferFrom(address from,address to,uint256 val) -// - an array of function abis: "[{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_orderId\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"_maxCost\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_fees\",\"type\":\"address\"}],\"name\":\"fillOrKillOrder\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"}]" -// - or a single function abi: "{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_orderId\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"_maxCost\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_fees\",\"type\":\"address\"}],\"name\":\"fillOrKillOrder\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"}" -// And it must always return it encoded, like this: -// - transferFrom(address,address,uint256) -// making sure that the method matches the returned one -func getMethodFromAbi(abi string, method string) (string, []string, error) { - // - // First attempt to parse `abi` string as a plain method abi - // ie. transferFrom(address,address,uint256) - // - - // Handle the case for already encoded method abi. - // NOTE: we do not need the know the `method` argument here. - abi = strings.TrimSpace(abi) - if len(abi) > 0 && strings.Contains(abi, "(") && abi[len(abi)-1] == ')' { - // NOTE: even though the ethcoder function is `ParseEventDef`, designed for event type parsing - // the abi format for a single function structure is the same, so it works. Perhaps we will rename - // `ParseEventDef` in the future, or just add another method with a different name. - eventDef, err := ethcoder.ParseEventDef(abi) - if err != nil { - return "", nil, err - } - return eventDef.Sig, eventDef.ArgNames, nil - } - - // - // If above didn't work, attempt to parse `abi` string as - // a JSON object of the full abi definition - // - - type FunctionAbi struct { - Name string `json:"name"` - Type string `json:"type"` - Inputs []struct { - InternalType string `json:"internalType"` - Name string `json:"name"` - Type string `json:"type"` - } `json:"inputs"` - } - - // Handle array of function abis and single function abi - var abis []FunctionAbi - if strings.HasPrefix(abi, "[") { - if err := json.Unmarshal([]byte(abi), &abis); err != nil { - return "", nil, err - } - } else { - var singleAbi FunctionAbi - if err := json.Unmarshal([]byte(abi), &singleAbi); err != nil { - return "", nil, err - } - abis = append(abis, singleAbi) - } - - // Find the correct method and encode it - for _, fnAbi := range abis { - if fnAbi.Name == method { - var paramTypes []string - order := make([]string, len(fnAbi.Inputs)) - for i, input := range fnAbi.Inputs { - paramTypes = append(paramTypes, input.Type) - order[i] = input.Name - } - return method + "(" + strings.Join(paramTypes, ",") + ")", order, nil - } - } - - return "", nil, errors.New("Method not found in ABI") -} diff --git a/intents/intent_data_transaction_delayed_abi_test.go b/intents/intent_data_transaction_delayed_abi_test.go index 12d5ff4f..9284ab58 100644 --- a/intents/intent_data_transaction_delayed_abi_test.go +++ b/intents/intent_data_transaction_delayed_abi_test.go @@ -4,65 +4,9 @@ import ( "encoding/json" "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) -func TestGetMethodFromABI(t *testing.T) { - // From ABI, alone, in array - res, order, err := getMethodFromAbi(`[{"name":"transfer","type":"function","inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}]}]`, "transfer") - assert.Nil(t, err) - - assert.Equal(t, "transfer(address,uint256)", res) - assert.Equal(t, []string{"_to", "_value"}, order) - - // From ABI, alone, as object - res, order, err = getMethodFromAbi(`{"name":"transfer","type":"function","inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}]}`, "transfer") - assert.Nil(t, err) - - assert.Equal(t, "transfer(address,uint256)", res) - assert.Equal(t, []string{"_to", "_value"}, order) - - // From ABI, with many args - res, order, err = getMethodFromAbi(`[{"inputs":[{"internalType":"bytes32","name":"_orderId","type":"bytes32"},{"internalType":"uint256","name":"_maxCost","type":"uint256"},{"internalType":"address[]","name":"_fees","type":"address[]"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"fillOrKillOrder","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_val","type":"uint256"},{"internalType":"string","name":"_data","type":"string"}],"name":"notExpired","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"otherMethods","outputs":[],"stateMutability":"nonpayable","type":"function"}]`, "fillOrKillOrder") - assert.Nil(t, err) - - assert.Equal(t, "fillOrKillOrder(bytes32,uint256,address[],bytes)", res) - assert.Equal(t, []string{"_orderId", "_maxCost", "_fees", "_data"}, order) - - res, order, err = getMethodFromAbi(`[{"inputs":[{"internalType":"bytes32","name":"_orderId","type":"bytes32"},{"internalType":"uint256","name":"_maxCost","type":"uint256"},{"internalType":"address[]","name":"_fees","type":"address[]"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"fillOrKillOrder","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_val","type":"uint256"},{"internalType":"string","name":"_data","type":"string"}],"name":"notExpired","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"otherMethods","outputs":[],"stateMutability":"nonpayable","type":"function"}]`, "notExpired") - assert.Nil(t, err) - - assert.Equal(t, "notExpired(uint256,string)", res) - assert.Equal(t, []string{"_val", "_data"}, order) - - res, order, err = getMethodFromAbi(`[{"inputs":[{"internalType":"bytes32","name":"_orderId","type":"bytes32"},{"internalType":"uint256","name":"_maxCost","type":"uint256"},{"internalType":"address[]","name":"_fees","type":"address[]"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"fillOrKillOrder","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_val","type":"uint256"},{"internalType":"string","name":"_data","type":"string"}],"name":"notExpired","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"otherMethods","outputs":[],"stateMutability":"nonpayable","type":"function"}]`, "otherMethods") - assert.Nil(t, err) - - assert.Equal(t, "otherMethods()", res) - assert.Equal(t, []string{}, order) - - // From plain method, without named args - res, order, err = getMethodFromAbi(`transfer(address,uint256)`, "transfer") - assert.Nil(t, err) - - assert.Equal(t, "transfer(address,uint256)", res) - assert.Equal(t, []string{"arg1", "arg2"}, order) - - // From plain method, with named args - res, order, err = getMethodFromAbi(`transfer(address _to,uint256 _value, bytes _mas)`, "transfer") - assert.Nil(t, err) - - assert.Equal(t, "transfer(address,uint256,bytes)", res) - assert.Equal(t, []string{"_to", "_value", "_mas"}, order) - - // Mixed plain method should return nil order - res, order, err = getMethodFromAbi(`transfer(address _to,uint256, bytes _mas)`, "transfer") - - assert.Nil(t, err) - assert.Equal(t, "transfer(address,uint256,bytes)", res) - assert.Equal(t, []string{"_to", "arg2", "_mas"}, order) -} - func TestEncodeDelayedABI(t *testing.T) { // Encode simple transferFrom, not named res, err := EncodeDelayedABI(&delayedEncodeType{ @@ -71,8 +15,8 @@ func TestEncodeDelayedABI(t *testing.T) { Args: json.RawMessage(`["0x0dc9603d4da53841C1C83f3B550C6143e60e0425","0x0dc9603d4da53841C1C83f3B550C6143e60e0425","100"]`), }) - assert.Nil(t, err) - assert.Equal(t, res, "0x23b872dd0000000000000000000000000dc9603d4da53841c1c83f3b550c6143e60e04250000000000000000000000000dc9603d4da53841c1c83f3b550c6143e60e04250000000000000000000000000000000000000000000000000000000000000064") + require.Nil(t, err) + require.Equal(t, "0x23b872dd0000000000000000000000000dc9603d4da53841c1c83f3b550c6143e60e04250000000000000000000000000dc9603d4da53841c1c83f3b550c6143e60e04250000000000000000000000000000000000000000000000000000000000000064", res) // Encode simple transferFrom, named res, err = EncodeDelayedABI(&delayedEncodeType{ @@ -81,8 +25,8 @@ func TestEncodeDelayedABI(t *testing.T) { Args: json.RawMessage(`{"_from": "0x0dc9603d4da53841C1C83f3B550C6143e60e0425", "_value": "100", "_to": "0x0dc9603d4da53841C1C83f3B550C6143e60e0425"}`), }) - assert.Nil(t, err) - assert.Equal(t, res, "0x23b872dd0000000000000000000000000dc9603d4da53841c1c83f3b550c6143e60e04250000000000000000000000000dc9603d4da53841c1c83f3b550c6143e60e04250000000000000000000000000000000000000000000000000000000000000064") + require.Nil(t, err) + require.Equal(t, "0x23b872dd0000000000000000000000000dc9603d4da53841c1c83f3b550c6143e60e04250000000000000000000000000dc9603d4da53841c1c83f3b550c6143e60e04250000000000000000000000000000000000000000000000000000000000000064", res) // Encode simple transferFrom, not named, passed as function res, err = EncodeDelayedABI(&delayedEncodeType{ @@ -91,8 +35,8 @@ func TestEncodeDelayedABI(t *testing.T) { Args: json.RawMessage(`["0x13915b1ea28Fd2E8197c88ff9D2422182E83bf25","0x4Ad47F1611c78C824Ff3892c4aE1CC04637D6462","5192381927398174182391237"]`), }) - assert.Nil(t, err) - assert.Equal(t, res, "0x23b872dd00000000000000000000000013915b1ea28fd2e8197c88ff9d2422182e83bf250000000000000000000000004ad47f1611c78c824ff3892c4ae1cc04637d6462000000000000000000000000000000000000000000044b87969b06250e50bdc5") + require.Nil(t, err) + require.Equal(t, "0x23b872dd00000000000000000000000013915b1ea28fd2e8197c88ff9d2422182e83bf250000000000000000000000004ad47f1611c78c824ff3892c4ae1cc04637d6462000000000000000000000000000000000000000000044b87969b06250e50bdc5", res) // Encode simple transferFrom, named, passed as function res, err = EncodeDelayedABI(&delayedEncodeType{ @@ -101,16 +45,19 @@ func TestEncodeDelayedABI(t *testing.T) { Args: json.RawMessage(`{"_from": "0x13915b1ea28Fd2E8197c88ff9D2422182E83bf25", "_value": "5192381927398174182391237", "_to": "0x4Ad47F1611c78C824Ff3892c4aE1CC04637D6462"}`), }) - assert.Nil(t, err) - assert.Equal(t, res, "0x23b872dd00000000000000000000000013915b1ea28fd2e8197c88ff9d2422182e83bf250000000000000000000000004ad47f1611c78c824ff3892c4ae1cc04637d6462000000000000000000000000000000000000000000044b87969b06250e50bdc5") + require.Nil(t, err) + require.Equal(t, "0x23b872dd00000000000000000000000013915b1ea28fd2e8197c88ff9d2422182e83bf250000000000000000000000004ad47f1611c78c824ff3892c4ae1cc04637d6462000000000000000000000000000000000000000000044b87969b06250e50bdc5", res) // Encode nested bytes, passed as function + + // TODO/NOTE: this makes no sense, we are making up an abi type of transferFrom just to encode a uint256 nestedEncodeType1 := &delayedEncodeType{ Abi: `transferFrom(uint256)`, Func: "transferFrom", Args: json.RawMessage(`["481923749816926378123"]`), } + // TODO/NOTE: this makes no sense, we are making up a random abi just so we can encode a string.. nestedEncodeType2 := &delayedEncodeType{ Abi: `hola(string)`, Func: "hola", @@ -118,28 +65,26 @@ func TestEncodeDelayedABI(t *testing.T) { } net1jsn, err := json.Marshal(nestedEncodeType1) - assert.Nil(t, err) + require.Nil(t, err) net2jsn, err := json.Marshal(nestedEncodeType2) - assert.Nil(t, err) + require.Nil(t, err) res, err = EncodeDelayedABI(&delayedEncodeType{ Abi: `[{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"bytes","name":"_arg1","type":"bytes"},{"internalType":"bytes","name":"_arg2","type":"bytes"}],"name":"caller","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_orderId","type":"bytes32"},{"internalType":"uint256","name":"_maxCost","type":"uint256"},{"internalType":"address[]","name":"_fees","type":"address[]"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"fillOrKillOrder","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_val","type":"uint256"},{"internalType":"string","name":"_data","type":"string"}],"name":"notExpired","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"s","outputs":[],"stateMutability":"nonpayable","type":"function"}]`, Func: "caller", Args: json.RawMessage(`{"addr": "0x13915b1ea28Fd2E8197c88ff9D2422182E83bf25", "_arg1": ` + string(net1jsn) + `, "_arg2": ` + string(net2jsn) + `}`), }) - - assert.Nil(t, err) - assert.Equal(t, res, "0x8b6701df00000000000000000000000013915b1ea28fd2e8197c88ff9d2422182e83bf25000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002477a11f7e00000000000000000000000000000000000000000000001a2009191df61e988b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000646ce8ea55000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000056d756e646f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") + require.Nil(t, err) + require.Equal(t, res, "0x8b6701df00000000000000000000000013915b1ea28fd2e8197c88ff9d2422182e83bf25000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002477a11f7e00000000000000000000000000000000000000000000001a2009191df61e988b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000646ce8ea55000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000056d756e646f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") // Fail passing named args to non-named abi - res, err = EncodeDelayedABI(&delayedEncodeType{ + _, err = EncodeDelayedABI(&delayedEncodeType{ Abi: `transferFrom(address,uint256)`, Func: "transferFrom", Args: json.RawMessage(`{"_from": "0x13915b1ea28Fd2E8197c88ff9D2422182E83bf25", "_value": "5192381927398174182391237", "_to": "0x4Ad47F1611c78C824Ff3892c4aE1CC04637D6462"}`), }) - - assert.NotNil(t, err) + require.NotNil(t, err) // error is expected // Accept passing ordened args to named abi res, err = EncodeDelayedABI(&delayedEncodeType{ @@ -147,7 +92,6 @@ func TestEncodeDelayedABI(t *testing.T) { Func: "transferFrom", Args: json.RawMessage(`["0x13915b1ea28Fd2E8197c88ff9D2422182E83bf25", "0x4Ad47F1611c78C824Ff3892c4aE1CC04637D6462", "9"]`), }) - - assert.Nil(t, err) - assert.Equal(t, res, "0x23b872dd00000000000000000000000013915b1ea28fd2e8197c88ff9d2422182e83bf250000000000000000000000004ad47f1611c78c824ff3892c4ae1cc04637d64620000000000000000000000000000000000000000000000000000000000000009") + require.Nil(t, err) + require.Equal(t, "0x23b872dd00000000000000000000000013915b1ea28fd2e8197c88ff9d2422182e83bf250000000000000000000000004ad47f1611c78c824ff3892c4ae1cc04637d64620000000000000000000000000000000000000000000000000000000000000009", res) } diff --git a/intents/intent_data_transaction_ext.go b/intents/intent_data_transaction_ext.go index 6d02c346..e7aa50d2 100644 --- a/intents/intent_data_transaction_ext.go +++ b/intents/intent_data_transaction_ext.go @@ -54,6 +54,9 @@ func (p *IntentDataSendTransaction) ExpectedValuesFor(txRaw *json.RawMessage) (* } to := common.HexToAddress(tx.To) + if tx.Value == "" { + tx.Value = "0" + } value, ok := sequence.ParseHexOrDec(tx.Value) if !ok { return nil, fmt.Errorf("invalid value '%s'", tx.Value) @@ -196,6 +199,43 @@ func (p *IntentDataSendTransaction) ExpectedValuesFor(txRaw *json.RawMessage) (* Data: encodedData, }, nil + case "contractCall": + var tx TransactionContractCall + + err := json.Unmarshal(*txRaw, &tx) + if err != nil { + return nil, err + } + + nst := &contractCallType{} + nst.Abi = tx.Data.Abi + if tx.Data.Func != nil { + nst.Func = *tx.Data.Func + } + nst.Args = tx.Data.Args + + encoded, err := EncodeContractCall(nst) + if err != nil { + return nil, err + } + + to := common.HexToAddress(tx.To) + if tx.Value == "" { + tx.Value = "0" + } + value, ok := sequence.ParseHexOrDec(tx.Value) + if !ok { + return nil, fmt.Errorf("invalid value '%s'", tx.Value) + } + + return &ExpectedValuesForTransaction{ + To: &to, + Value: value, + Data: common.FromHex(encoded), + }, nil + + // NOTE: 'delayedEncode' is deprecated, should use 'contractCall' instead, we're leaving + // it here for backwards compatibility. case "delayedEncode": var tx TransactionDelayedEncode @@ -214,7 +254,6 @@ func (p *IntentDataSendTransaction) ExpectedValuesFor(txRaw *json.RawMessage) (* if err != nil { return nil, err } - to := common.HexToAddress(tx.To) value, ok := sequence.ParseHexOrDec(tx.Value) if !ok { @@ -281,7 +320,7 @@ func (p *IntentDataSendTransaction) IsValidInterpretation(subdigest common.Hash, } for i, txn := range txns { - if txn.DelegateCall != false { + if txn.DelegateCall { return false } diff --git a/intents/intent_data_transaction_ext_test.go b/intents/intent_data_transaction_ext_test.go index 83978e65..f75c40a4 100644 --- a/intents/intent_data_transaction_ext_test.go +++ b/intents/intent_data_transaction_ext_test.go @@ -8,13 +8,297 @@ import ( "github.com/0xsequence/ethkit/ethwallet" "github.com/0xsequence/ethkit/go-ethereum/common" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/0xsequence/go-sequence" ) func TestRecoverTransactionIntent(t *testing.T) { + data := `{ + "version": "1", + "name": "sendTransaction", + "issued": 0, + "expires": 0, + "data": { + "code": "sendTransaction", + "identifier": "test-identifier", + "wallet": "0xD67FC48b298B09Ed3D03403d930769C527186c4e", + "network": "10", + "transactions": [ + { + "type": "transaction", + "to": "0x479F6a5b0C1728947318714963a583C56A78366A", + "value": "39381", + "data": "0x3251ba32" + }, + { + "type": "erc20send", + "tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "to": "0x7b1Bd3474D789e18e2E329E2c53F819B6E687b4A", + "value": "1000" + }, + { + "type": "erc721send", + "tokenAddress": "0xF87E31492Faf9A91B02Ee0dEAAd50d51d56D5d4d", + "to": "0x17fFA2d95b58228e1ECb0C6Ac25A6EfD20BA08E4", + "id": "7", + "safe": true, + "data": "0x112233" + }, + { + "type": "erc1155send", + "tokenAddress": "0x631998e91476da5b870d741192fc5cbc55f5a52e", + "to": "0x91E8aC543C5fEDf9F3Ef8b9dA1500dB84305681F", + "vals": [ + { + "id": "2", + "amount": "5" + }, + { + "id": "500", + "amount": "1" + } + ], + "data": "0x223344" + }, + { + "type": "contractCall", + "to": "0x140d72763D1ce39Ad4E2e73EC6e8FC53E5b73B64", + "data": { + "abi": "fillOrKillOrder(uint256 orderId, uint256 maxCost, address[] fees, bytes data)", + "args": [ + "48774435471364917511246724398022004900255301025912680232738918790354204737320", + "1000000000000000000", + "[\"0x8541D65829f98f7D71A4655cCD7B2bB8494673bF\"]", + { + "abi": "notExpired(uint256,string)", + "args": [ + "1600000000", + "Nov 1st, 2020" + ] + } + ] + } + } + ] + }, + "signatures": [] + }` + + intent := &Intent{} + err := json.Unmarshal([]byte(data), intent) + require.Nil(t, err) + + require.Equal(t, "1", intent.Version) + require.Equal(t, IntentName_sendTransaction, intent.Name) + + hash, err := intent.Hash() + require.Nil(t, err) + require.NotNil(t, common.Bytes2Hex(hash)) + + intent.IssuedAt = uint64(time.Now().Unix()) + intent.ExpiresAt = uint64(time.Now().Unix()) + 60 + + wallet, err := ethwallet.NewWalletFromRandomEntropy() + require.Nil(t, err) + + session := NewSessionP256K1(wallet) + + err = session.Sign(intent) + require.Nil(t, err) + + intentTyped, err := NewIntentTypedFromIntent[IntentDataSendTransaction](intent) + require.Nil(t, err) + + signers := intent.Signers() + require.Equal(t, 1, len(signers)) + require.Equal(t, "0x"+common.Bytes2Hex(append([]byte{0x00}, wallet.Address().Bytes()...)), signers[0]) + + require.NoError(t, intentTyped.IsValid()) + + sendTransactionData := intentTyped.Data + + // Generate transactions as sequence.Wallet would + nonce, err := sendTransactionData.Nonce() + require.Nil(t, err) + + chainID := big.NewInt(10) + + transactions := make(sequence.Transactions, 0) + transactions = append(transactions, &sequence.Transaction{ + DelegateCall: false, + RevertOnError: true, + GasLimit: big.NewInt(21000), + To: common.HexToAddress("0x479F6a5b0C1728947318714963a583C56A78366A"), + Value: big.NewInt(39381), + Data: common.FromHex("0x3251ba32"), + }) + + transactions = append(transactions, &sequence.Transaction{ + DelegateCall: false, + RevertOnError: true, + GasLimit: big.NewInt(21000), + To: common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"), + Value: big.NewInt(0), + Data: common.FromHex("0xa9059cbb0000000000000000000000007b1bd3474d789e18e2e329e2c53f819b6e687b4a00000000000000000000000000000000000000000000000000000000000003e8"), + }) + + transactions = append(transactions, &sequence.Transaction{ + DelegateCall: false, + RevertOnError: true, + GasLimit: big.NewInt(21000), + To: common.HexToAddress("0xF87E31492Faf9A91B02Ee0dEAAd50d51d56D5d4d"), + Value: big.NewInt(0), + Data: common.FromHex("0xb88d4fde000000000000000000000000d67fc48b298b09ed3d03403d930769c527186c4e00000000000000000000000017ffa2d95b58228e1ecb0c6ac25a6efd20ba08e40000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000031122330000000000000000000000000000000000000000000000000000000000"), + }) + + transactions = append(transactions, &sequence.Transaction{ + DelegateCall: false, + RevertOnError: true, + GasLimit: big.NewInt(21000), + To: common.HexToAddress("0x631998e91476da5b870d741192fc5cbc55f5a52e"), + Value: big.NewInt(0), + Data: common.FromHex("0x2eb2c2d6000000000000000000000000d67fc48b298b09ed3d03403d930769c527186c4e00000000000000000000000091e8ac543c5fedf9f3ef8b9da1500db84305681f00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000032233440000000000000000000000000000000000000000000000000000000000"), + }) + + transactions = append(transactions, &sequence.Transaction{ + DelegateCall: false, + RevertOnError: true, + GasLimit: big.NewInt(0), + To: common.HexToAddress("0x140d72763D1ce39Ad4E2e73EC6e8FC53E5b73B64"), + Value: big.NewInt(0), + Data: common.FromHex("0x6365f1646bd55a2877890bd58871eefe886770a7734077a74981910a75d7b1f044b5bf280000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008541d65829f98f7d71a4655ccd7b2bb8494673bf000000000000000000000000000000000000000000000000000000000000008446c421fa000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000d4e6f76203173742c20323032300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + }) + + bundle := sequence.Transaction{ + Transactions: transactions, + Nonce: nonce, + } + + digest, err := bundle.Digest() + require.Nil(t, err) + + subdigest, err := sequence.SubDigest( + chainID, + common.HexToAddress("0xD67FC48b298B09Ed3D03403d930769C527186c4e"), + digest, + ) + require.Nil(t, err) + + require.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + + // changing any transaction value should invalidate the interpretation + for i := range transactions { + prev := transactions[i].Value + transactions[i].Value = big.NewInt(123) + require.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + transactions[i].Value = prev + require.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + } + + // changing any transaction data should invalidate the interpretation + for i := range transactions { + prev := transactions[i].Data + transactions[i].Data = common.Hex2Bytes("0x1234") + require.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + transactions[i].Data = prev + require.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + } + + // changing any to address should invalidate the interpretation + for i := range transactions { + prev := transactions[i].To + transactions[i].To = common.HexToAddress("0xd1333D70A344c26041a869077381209462e586F8") + require.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + transactions[i].To = prev + require.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + } + + // setting any delegate call should invalidate the interpretation + for i := range transactions { + transactions[i].DelegateCall = true + require.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + transactions[i].DelegateCall = false + require.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + } + + // changing revert on error should NOT invalidate the interpretation + for i := range transactions { + transactions[i].RevertOnError = false + + nxtBundle := sequence.Transaction{ + Transactions: transactions, + Nonce: nonce, + } + nxtdigest, err := nxtBundle.Digest() + require.Nil(t, err) + nxtsubdigest, err := sequence.SubDigest( + chainID, + common.HexToAddress("0xD67FC48b298B09Ed3D03403d930769C527186c4e"), + nxtdigest, + ) + require.Nil(t, err) + + require.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(nxtsubdigest), transactions, nonce)) + transactions[i].RevertOnError = true + } + + // changing any gas limit should NOT invalidate the interpretation + for i := range transactions { + prev := transactions[i].GasLimit + transactions[i].GasLimit = big.NewInt(123) + + nxtBundle := sequence.Transaction{ + Transactions: transactions, + Nonce: nonce, + } + nxtdigest, err := nxtBundle.Digest() + require.Nil(t, err) + nxtsubdigest, err := sequence.SubDigest( + chainID, + common.HexToAddress("0xD67FC48b298B09Ed3D03403d930769C527186c4e"), + nxtdigest, + ) + require.Nil(t, err) + + require.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(nxtsubdigest), transactions, nonce)) + + transactions[i].GasLimit = prev + } + + // changing the nonce should invalidate the interpretation + nxtBundle := sequence.Transaction{ + Transactions: transactions, + Nonce: big.NewInt(123), + } + nxtdigest, err := nxtBundle.Digest() + require.Nil(t, err) + nxtsubdigest, err := sequence.SubDigest( + chainID, + common.HexToAddress("0xD67FC48b298B09Ed3D03403d930769C527186c4e"), + nxtdigest, + ) + require.Nil(t, err) + require.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(nxtsubdigest), transactions, big.NewInt(123))) + + // removing a transaction should invalidate the interpretation + require.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions[1:], nonce)) + + // adding an extra transaction should invalidate the interpretation + transactions = append(transactions, &sequence.Transaction{ + DelegateCall: false, + RevertOnError: true, + GasLimit: big.NewInt(21000), + To: common.HexToAddress("0x479F6a5b0C1728947318714963a583C56A78366A"), + Value: big.NewInt(39381), + Data: common.FromHex("0x3251ba32"), + }) + + require.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) +} + +// NOTE: DelayedEncode is deprecated, see TestRecoverTransactionIntent below which uses contractCall +func Test2RecoverTransactionIntentWithDelayedEncode(t *testing.T) { data := `{ "version": "1", "name": "sendTransaction", @@ -91,14 +375,14 @@ func TestRecoverTransactionIntent(t *testing.T) { intent := &Intent{} err := json.Unmarshal([]byte(data), intent) - assert.Nil(t, err) + require.Nil(t, err) - assert.Equal(t, "1", intent.Version) - assert.Equal(t, IntentName_sendTransaction, intent.Name) + require.Equal(t, "1", intent.Version) + require.Equal(t, IntentName_sendTransaction, intent.Name) hash, err := intent.Hash() - assert.Nil(t, err) - assert.NotNil(t, common.Bytes2Hex(hash)) + require.Nil(t, err) + require.NotNil(t, common.Bytes2Hex(hash)) intent.IssuedAt = uint64(time.Now().Unix()) intent.ExpiresAt = uint64(time.Now().Unix()) + 60 @@ -112,11 +396,11 @@ func TestRecoverTransactionIntent(t *testing.T) { require.Nil(t, err) intentTyped, err := NewIntentTypedFromIntent[IntentDataSendTransaction](intent) - assert.Nil(t, err) + require.Nil(t, err) signers := intent.Signers() - assert.Equal(t, 1, len(signers)) - assert.Equal(t, "0x"+common.Bytes2Hex(append([]byte{0x00}, wallet.Address().Bytes()...)), signers[0]) + require.Equal(t, 1, len(signers)) + require.Equal(t, "0x"+common.Bytes2Hex(append([]byte{0x00}, wallet.Address().Bytes()...)), signers[0]) require.NoError(t, intentTyped.IsValid()) @@ -124,7 +408,7 @@ func TestRecoverTransactionIntent(t *testing.T) { // Generate transactions as sequence.Wallet would nonce, err := sendTransactionData.Nonce() - assert.Nil(t, err) + require.Nil(t, err) chainID := big.NewInt(10) @@ -180,50 +464,50 @@ func TestRecoverTransactionIntent(t *testing.T) { } digest, err := bundle.Digest() - assert.Nil(t, err) + require.Nil(t, err) subdigest, err := sequence.SubDigest( chainID, common.HexToAddress("0xD67FC48b298B09Ed3D03403d930769C527186c4e"), digest, ) - assert.Nil(t, err) + require.Nil(t, err) - assert.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + require.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) // changing any transaction value should invalidate the interpretation for i := range transactions { prev := transactions[i].Value transactions[i].Value = big.NewInt(123) - assert.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + require.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) transactions[i].Value = prev - assert.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + require.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) } // changing any transaction data should invalidate the interpretation for i := range transactions { prev := transactions[i].Data transactions[i].Data = common.Hex2Bytes("0x1234") - assert.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + require.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) transactions[i].Data = prev - assert.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + require.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) } // changing any to address should invalidate the interpretation for i := range transactions { prev := transactions[i].To transactions[i].To = common.HexToAddress("0xd1333D70A344c26041a869077381209462e586F8") - assert.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + require.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) transactions[i].To = prev - assert.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + require.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) } // setting any delegate call should invalidate the interpretation for i := range transactions { transactions[i].DelegateCall = true - assert.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + require.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) transactions[i].DelegateCall = false - assert.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + require.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) } // changing revert on error should NOT invalidate the interpretation @@ -235,15 +519,15 @@ func TestRecoverTransactionIntent(t *testing.T) { Nonce: nonce, } nxtdigest, err := nxtBundle.Digest() - assert.Nil(t, err) + require.Nil(t, err) nxtsubdigest, err := sequence.SubDigest( chainID, common.HexToAddress("0xD67FC48b298B09Ed3D03403d930769C527186c4e"), nxtdigest, ) - assert.Nil(t, err) + require.Nil(t, err) - assert.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(nxtsubdigest), transactions, nonce)) + require.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(nxtsubdigest), transactions, nonce)) transactions[i].RevertOnError = true } @@ -257,15 +541,15 @@ func TestRecoverTransactionIntent(t *testing.T) { Nonce: nonce, } nxtdigest, err := nxtBundle.Digest() - assert.Nil(t, err) + require.Nil(t, err) nxtsubdigest, err := sequence.SubDigest( chainID, common.HexToAddress("0xD67FC48b298B09Ed3D03403d930769C527186c4e"), nxtdigest, ) - assert.Nil(t, err) + require.Nil(t, err) - assert.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(nxtsubdigest), transactions, nonce)) + require.True(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(nxtsubdigest), transactions, nonce)) transactions[i].GasLimit = prev } @@ -276,17 +560,17 @@ func TestRecoverTransactionIntent(t *testing.T) { Nonce: big.NewInt(123), } nxtdigest, err := nxtBundle.Digest() - assert.Nil(t, err) + require.Nil(t, err) nxtsubdigest, err := sequence.SubDigest( chainID, common.HexToAddress("0xD67FC48b298B09Ed3D03403d930769C527186c4e"), nxtdigest, ) - assert.Nil(t, err) - assert.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(nxtsubdigest), transactions, big.NewInt(123))) + require.Nil(t, err) + require.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(nxtsubdigest), transactions, big.NewInt(123))) // removing a transaction should invalidate the interpretation - assert.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions[1:], nonce)) + require.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions[1:], nonce)) // adding an extra transaction should invalidate the interpretation transactions = append(transactions, &sequence.Transaction{ @@ -298,5 +582,5 @@ func TestRecoverTransactionIntent(t *testing.T) { Data: common.FromHex("0x3251ba32"), }) - assert.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) + require.False(t, sendTransactionData.IsValidInterpretation(common.BytesToHash(subdigest), transactions, nonce)) } diff --git a/utils.go b/utils.go index 3369105c..57438c95 100644 --- a/utils.go +++ b/utils.go @@ -92,6 +92,5 @@ func ParseHexOrDec(s string) (*big.Int, bool) { if len(s) > 2 && s[0:2] == "0x" { return new(big.Int).SetString(s[2:], 16) } - return new(big.Int).SetString(s, 10) }