From 4ce8f44ef53395a3cb870dbd4fa9fa000ce59e94 Mon Sep 17 00:00:00 2001 From: Nevio Date: Fri, 31 May 2024 12:29:59 +0200 Subject: [PATCH 01/11] Enabling parsing contracts for solidity 0.4+ --- contracts/parser.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/parser.go b/contracts/parser.go index 9603592d..f81aab44 100644 --- a/contracts/parser.go +++ b/contracts/parser.go @@ -49,9 +49,10 @@ func (c *Contract) Parse(ctx context.Context) error { // we are getting into issues where we are not capable ATM to parse contracts with < 0.6.0 version. // Because of it, we are going to disable all functionality for this particular contract related to // source code parsing. :( In the future we should sort this out, but right now, MVP is the most important thing. - if utils.IsSemanticVersionLowerOrEqualTo(c.descriptor.CompilerVersion, utils.SemanticVersion{Major: 0, Minor: 5, Patch: 0}) { + if utils.IsSemanticVersionLowerOrEqualTo(c.descriptor.CompilerVersion, utils.SemanticVersion{Major: 0, Minor: 4, Patch: 0}) { // There are some contracts we want to ensure to go through parsing logic regardless of semantic versioning check // ETH: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 - 0.4.19 - WETH9 + // NOTE: Temporary allowing all of the 0.4+ if c.descriptor.Address != common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") { return fmt.Errorf("not supported compiler version (older version): %v", c.descriptor.CompilerVersion) } From 27ff8260f37d084d44463e6b9124bc96beba60f5 Mon Sep 17 00:00:00 2001 From: Nevio Date: Sun, 16 Jun 2024 16:27:16 +0200 Subject: [PATCH 02/11] Bunch of type fixes --- abi/constructor.go | 1 - abi/contract.go | 13 ++- accounts/account.go | 6 +- accounts/options.go | 4 +- ast/helpers.go | 33 +++++++- ast/parameter.go | 1 - ast/reference.go | 18 +++- ast/type_name.go | 45 ++++++++-- bindings/token.go | 4 +- bindings/uniswap_v2.go | 173 +++++++++++++++++++++++++++++++++++++++ bytecode/log.go | 18 ++-- bytecode/transaction.go | 7 +- contracts/constructor.go | 1 + ir/variables.go | 4 + utils/networks.go | 23 ++++++ utils/types.go | 5 ++ utils/wei.go | 35 ++++++++ 17 files changed, 364 insertions(+), 27 deletions(-) create mode 100644 bindings/uniswap_v2.go create mode 100644 utils/wei.go diff --git a/abi/constructor.go b/abi/constructor.go index a0568c79..43b9f3a8 100644 --- a/abi/constructor.go +++ b/abi/constructor.go @@ -2,7 +2,6 @@ package abi import ( "fmt" - "github.com/unpackdev/solgo/ir" ) diff --git a/abi/contract.go b/abi/contract.go index 04176b36..ddb95307 100644 --- a/abi/contract.go +++ b/abi/contract.go @@ -5,6 +5,7 @@ import ( ast_pb "github.com/unpackdev/protos/dist/go/ast" "github.com/unpackdev/solgo/ast" "github.com/unpackdev/solgo/ir" + "strings" ) // Contract represents a collection of Ethereum contract methods. @@ -128,10 +129,18 @@ func (b *Builder) buildMethodIO(method MethodIO, typeDescr *ast.TypeDescription) method.Inputs = append(method.Inputs, inputList...) method.Outputs = append(method.Outputs, outputList...) case "contract": - method.Type = "address" + if strings.ContainsAny(typeDescr.GetString(), "[]") { + method.Type = "address[]" + } else { + method.Type = "address" + } method.InternalType = typeDescr.GetString() case "enum": - method.Type = "uint8" + if strings.ContainsAny(typeDescr.GetString(), "[]") { + method.Type = "uint8[]" + } else { + method.Type = "uint8" + } method.InternalType = typeDescr.GetString() case "struct": return b.resolver.ResolveStructType(typeDescr) diff --git a/accounts/account.go b/accounts/account.go index a7f2d2b3..1bf44aad 100644 --- a/accounts/account.go +++ b/accounts/account.go @@ -28,7 +28,7 @@ const ( // It embeds ClientPool for network interactions and KeyStore for account management. // It also includes fields for account details, network information, and additional tags. type Account struct { - client *clients.Client `json:"-" yaml:"-"` // Client for Ethereum client interactions + client *clients.Client *keystore.KeyStore `json:"-" yaml:"-"` // KeyStore for managing account keys Address common.Address `json:"address" yaml:"address"` // Ethereum address of the account Type utils.AccountType `json:"type" yaml:"type"` // Account type @@ -79,7 +79,7 @@ func (a *Account) GetClient() *clients.Client { // It does not affect the real balance on the Ethereum network. func (a *Account) SetAccountBalance(ctx context.Context, amount *big.Int) error { amountHex := common.Bytes2Hex(amount.Bytes()) - return a.client.GetRpcClient().Call(nil, "anvil_setBalance", a.GetAddress(), amountHex) + return a.client.GetRpcClient().CallContext(ctx, nil, "anvil_setBalance", a.GetAddress(), amountHex) } // Balance retrieves the account's balance from the Ethereum network at a specified block number. @@ -236,6 +236,8 @@ func LoadAccount(path string) (*Account, error) { if err != nil { return nil, err } + account.Address = account.KeystoreAccount.Address + account.Type = utils.KeystoreAccountType return &account, nil } diff --git a/accounts/options.go b/accounts/options.go index 3c04ff0a..1afa70f4 100644 --- a/accounts/options.go +++ b/accounts/options.go @@ -6,9 +6,9 @@ import "github.com/unpackdev/solgo/utils" type Options struct { // KeystorePath specifies the file system path to the directory where the keystore files are stored. // The keystore is used to securely store the private keys of Ethereum accounts. - KeystorePath string `json:"keystore_path" yaml:"keystore_path"` + KeystorePath string `json:"keystore_path" yaml:"keystorePath"` // SupportedNetworks lists the Ethereum based networks that the account manager will interact with. // Each network has a corresponding keystore and set of account configurations. - SupportedNetworks []utils.Network `json:"supported_networks" yaml:"supported_networks"` + SupportedNetworks []utils.Network `json:"supported_networks" yaml:"networks"` } diff --git a/ast/helpers.go b/ast/helpers.go index 40996e19..a8dc5e32 100644 --- a/ast/helpers.go +++ b/ast/helpers.go @@ -126,8 +126,30 @@ func normalizeTypeDescription(typeName string) (string, string) { } } +// builtInTypePrefixes is a set of known built-in type prefixes. +var builtInTypePrefixes = []string{ + "uint", + "int", + "bool", + "bytes", + "string", + "address", + "addresspayable", + "tuple", +} + +// isBuiltInType checks if a type name is a built-in type by its prefix. +func isBuiltInType(typeName string) bool { + for _, prefix := range builtInTypePrefixes { + if strings.HasPrefix(typeName, prefix) { + return true + } + } + return false +} + // normalizeTypeDescriptionWithStatus normalizes type names and generates corresponding type identifiers. -// Returns true if normalization occured. +// Returns true if normalization occurred. func normalizeTypeDescriptionWithStatus(typeName string) (string, string, bool) { isArray := strings.Contains(typeName, "[") && strings.Contains(typeName, "]") isSlice := strings.HasSuffix(typeName, "[]") @@ -137,16 +159,25 @@ func normalizeTypeDescriptionWithStatus(typeName string) (string, string, bool) case isArray: numberPart := typeName[strings.Index(typeName, "[")+1 : strings.Index(typeName, "]")] typePart := typeName[:strings.Index(typeName, "[")] + if !isBuiltInType(typePart) { + return typeName, fmt.Sprintf("t_%s", typeName), false + } normalizedTypePart := normalizeTypeName(typePart) return normalizedTypePart + "[" + numberPart + "]", fmt.Sprintf("t_%s_array", normalizedTypePart), true case isSlice: typePart := typeName[:len(typeName)-2] + if !isBuiltInType(typePart) { + return typeName, fmt.Sprintf("t_%s", typeName), false + } normalizedTypePart := normalizeTypeName(typePart) return normalizedTypePart + "[]", fmt.Sprintf("t_%s_slice", normalizedTypePart), true case isPrefixSlice: typePart := typeName[2:] + if !isBuiltInType(typePart) { + return typeName, fmt.Sprintf("t_%s", typeName), false + } normalizedTypePart := normalizeTypeName(typePart) return "[]" + normalizedTypePart, fmt.Sprintf("t_%s_slice", normalizedTypePart), true diff --git a/ast/parameter.go b/ast/parameter.go index 6b431160..1c94c6d6 100644 --- a/ast/parameter.go +++ b/ast/parameter.go @@ -2,7 +2,6 @@ package ast import ( "github.com/goccy/go-json" - ast_pb "github.com/unpackdev/protos/dist/go/ast" "github.com/unpackdev/solgo/parser" ) diff --git a/ast/reference.go b/ast/reference.go index e6c2ed60..b0669464 100644 --- a/ast/reference.go +++ b/ast/reference.go @@ -52,7 +52,17 @@ func (r *Resolver) GetUnprocessedCount() int { // ResolveByNode attempts to resolve a node by its name and returns the resolved Node and its TypeDescription. // If the node cannot be found, it is added to the UnprocessedNodes map for future resolution. func (r *Resolver) ResolveByNode(node Node[NodeType], name string) (int64, *TypeDescription) { - rNode, rNodeType := r.resolveByNode(name, node) + isSlice := strings.HasSuffix(name, "[]") + isPrefixSlice := strings.HasPrefix(name, "[]") + cleanedName := name + + if isSlice { + cleanedName = strings.TrimSuffix(name, "[]") + } else if isPrefixSlice { + cleanedName = strings.TrimPrefix(name, "[]") + } + + rNode, rNodeType := r.resolveByNode(cleanedName, node) // Node could not be found in this moment, we are going to see if we can discover it in the // future at the end of whole parsing process. @@ -62,6 +72,12 @@ func (r *Resolver) ResolveByNode(node Node[NodeType], name string) (int64, *Type Name: name, Node: node, } + } else { + if isSlice && !strings.Contains(rNodeType.TypeString, "[]") { + rNodeType.TypeString = rNodeType.TypeString + "[]" + } else if isPrefixSlice && !strings.Contains(rNodeType.TypeString, "[]") { + rNodeType.TypeString = "[]" + rNodeType.TypeString + } } return rNode, rNodeType diff --git a/ast/type_name.go b/ast/type_name.go index 197b06b0..cb06167c 100644 --- a/ast/type_name.go +++ b/ast/type_name.go @@ -62,8 +62,10 @@ func (t *TypeName) WithParentNode(p Node[NodeType]) { // SetReferenceDescriptor sets the reference descriptions of the TypeName node. func (t *TypeName) SetReferenceDescriptor(refId int64, refDesc *TypeDescription) bool { - t.ReferencedDeclaration = refId - t.TypeDescription = refDesc + if t.TypeDescription == nil { + t.ReferencedDeclaration = refId + t.TypeDescription = refDesc + } // Lets update the parent node as well in case that type description is not set... /* parentNodeId := t.GetSrc().GetParentIndex() @@ -370,7 +372,6 @@ func (t *TypeName) parseTypeName(unit *SourceUnit[Node[ast_pb.SourceUnit]], pare Length: int64(pathCtx.GetStop().GetStop() - pathCtx.GetStart().GetStart() + 1), ParentIndex: t.GetId(), }, - NodeType: ast_pb.NodeType_IDENTIFIER_PATH, } @@ -386,7 +387,7 @@ func (t *TypeName) parseTypeName(unit *SourceUnit[Node[ast_pb.SourceUnit]], pare } if found { - t.TypeDescription = &TypeDescription{ + t.PathNode.TypeDescription = &TypeDescription{ TypeIdentifier: normalizedTypeIdentifier, TypeString: normalizedTypeName, } @@ -400,6 +401,13 @@ func (t *TypeName) parseTypeName(unit *SourceUnit[Node[ast_pb.SourceUnit]], pare } } + // Alright lets now figure out main type description as it can be different such as + // PathNode vs PathNode[] + if refId, refTypeDescription := t.GetResolver().ResolveByNode(t, t.Name); refTypeDescription != nil { + t.ReferencedDeclaration = refId + t.TypeDescription = refTypeDescription + } + } else if ctx.TypeName() != nil { t.generateTypeName(unit, ctx.TypeName(), t, t) } else { @@ -438,7 +446,6 @@ func (t *TypeName) parseTypeName(unit *SourceUnit[Node[ast_pb.SourceUnit]], pare t.TypeDescription = refTypeDescription } } - } } @@ -515,17 +522,39 @@ func (t *TypeName) parseIdentifierPath(unit *SourceUnit[Node[ast_pb.SourceUnit]] } if found { - t.TypeDescription = &TypeDescription{ + t.PathNode.TypeDescription = &TypeDescription{ TypeIdentifier: normalizedTypeIdentifier, TypeString: normalizedTypeName, } } else { - if refId, refTypeDescription := t.GetResolver().ResolveByNode(t, identifierCtx.GetText()); refTypeDescription != nil { + if refId, refTypeDescription := t.GetResolver().ResolveByNode(t.PathNode, identifierCtx.GetText()); refTypeDescription != nil { t.PathNode.ReferencedDeclaration = refId + t.PathNode.TypeDescription = refTypeDescription + } + } + + bNormalizedTypeName, bNormalizedTypeIdentifier, bFound := normalizeTypeDescriptionWithStatus( + identifierCtx.GetText(), + ) + + // Alright lets now figure out main type description as it can be different such as + // PathNode vs PathNode[] + if bFound { + t.TypeDescription = &TypeDescription{ + TypeIdentifier: bNormalizedTypeIdentifier, + TypeString: bNormalizedTypeName, + } + } else { + if refId, refTypeDescription := t.GetResolver().ResolveByNode(t, t.Name); refTypeDescription != nil { t.ReferencedDeclaration = refId t.TypeDescription = refTypeDescription } } + + /* if t.Id == 1787 { + fmt.Println("HERE I AM") + utils.DumpNodeWithExit(t) + }*/ } } @@ -705,6 +734,7 @@ func (t *TypeName) generateTypeName(sourceUnit *SourceUnit[Node[ast_pb.SourceUni } else if specificCtx.IdentifierPath() != nil { typeName.NodeType = ast_pb.NodeType_USER_DEFINED_PATH_NAME t.parseIdentifierPath(sourceUnit, parentNode.GetId(), specificCtx.IdentifierPath().(*parser.IdentifierPathContext)) + } else { normalizedTypeName, normalizedTypeIdentifier := normalizeTypeDescription( @@ -777,6 +807,7 @@ func (t *TypeName) Parse(unit *SourceUnit[Node[ast_pb.SourceUnit]], fnNode Node[ case *antlr.TerminalNodeImpl: continue default: + expression := NewExpression(t.ASTBuilder) if expr := expression.ParseInterface(unit, fnNode, t.GetId(), ctx.Expression()); expr != nil { t.Expression = expr diff --git a/bindings/token.go b/bindings/token.go index ca66ca48..3219d048 100644 --- a/bindings/token.go +++ b/bindings/token.go @@ -42,8 +42,8 @@ func NewToken(ctx context.Context, network utils.Network, manager *Manager, opts // Now lets register all the bindings with the manager for _, opt := range opts { - for _, network := range opt.Networks { - if _, err := manager.RegisterBinding(network, opt.NetworkID, opt.Type, opt.Address, opt.ABI); err != nil { + for _, oNetwork := range opt.Networks { + if _, err := manager.RegisterBinding(oNetwork, opt.NetworkID, opt.Type, opt.Address, opt.ABI); err != nil { return nil, err } } diff --git a/bindings/uniswap_v2.go b/bindings/uniswap_v2.go new file mode 100644 index 00000000..8b5d587e --- /dev/null +++ b/bindings/uniswap_v2.go @@ -0,0 +1,173 @@ +package bindings + +import ( + "context" + "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/unpackdev/solgo/utils" + "math/big" +) + +const ( + UniswapV2Factory BindingType = "UniswapV2Factory" + UniswapV2Pair BindingType = "UniswapV2Pair" + UniswapV2Router BindingType = "UniswapV2Router" +) + +type UniswapV2Reserves struct { + Reserve0 *big.Int + Reserve1 *big.Int + BlockTimestampLast uint32 +} + +// UniswapV2 encapsulates data and functions for interacting with a blockchain Uniswap V2 contracts. +type UniswapV2 struct { + *Manager + network utils.Network + ctx context.Context + opts []*BindOptions +} + +func NewUniswapV2(ctx context.Context, network utils.Network, manager *Manager, opts []*BindOptions) (*UniswapV2, error) { + if opts == nil { + opts = DefaultUniswapV2BindOptions() + } + + for _, opt := range opts { + if err := opt.Validate(); err != nil { + return nil, err + } + } + + for _, opt := range opts { + for _, oNetwork := range opt.Networks { + if _, err := manager.RegisterBinding(oNetwork, opt.NetworkID, opt.Type, opt.Address, opt.ABI); err != nil { + return nil, err + } + } + } + + return &UniswapV2{ + network: network, + Manager: manager, + ctx: ctx, + opts: opts, + }, nil +} + +func (u *UniswapV2) GetAddress(bindingType BindingType) (common.Address, error) { + for _, opt := range u.opts { + if opt.Type == bindingType { + return opt.Address, nil + } + } + + return common.Address{}, fmt.Errorf("binding not found for type %s", bindingType) +} + +func (u *UniswapV2) WETH(ctx context.Context) (common.Address, error) { + result, err := u.Manager.CallContractMethod(ctx, u.network, UniswapV2Router, utils.ZeroAddress, "WETH") + if err != nil { + return common.Address{}, fmt.Errorf("failed to get WETH address: %w", err) + } + + return result.(common.Address), nil +} + +func (u *UniswapV2) GetPair(ctx context.Context, tokenA, tokenB common.Address) (common.Address, error) { + result, err := u.Manager.CallContractMethod(ctx, u.network, UniswapV2Factory, utils.ZeroAddress, "getPair", tokenA, tokenB) + if err != nil { + return common.Address{}, fmt.Errorf("failed to get pair: %w", err) + } + + pairAddress, ok := result.(common.Address) + if !ok { + return common.Address{}, fmt.Errorf("failed to assert result as common.Address - pair address") + } + + return pairAddress, nil +} + +func (u *UniswapV2) GetToken0(ctx context.Context, pairAddr common.Address) (common.Address, error) { + result, err := u.Manager.CallContractMethod(ctx, u.network, UniswapV2Pair, pairAddr, "token0") + if err != nil { + return common.Address{}, fmt.Errorf("failed to get token0 of a pair: %w", err) + } + + pairAddress, ok := result.(common.Address) + if !ok { + return common.Address{}, fmt.Errorf("failed to assert result as common.Address - token0 address") + } + + return pairAddress, nil +} + +func (u *UniswapV2) GetToken1(ctx context.Context, pairAddr common.Address) (common.Address, error) { + result, err := u.Manager.CallContractMethod(ctx, u.network, UniswapV2Pair, pairAddr, "token1") + if err != nil { + return common.Address{}, fmt.Errorf("failed to get token1 of a pair: %w", err) + } + + pairAddress, ok := result.(common.Address) + if !ok { + return common.Address{}, fmt.Errorf("failed to assert result as common.Address - token1 address") + } + + return pairAddress, nil +} + +func (u *UniswapV2) GetReserves(ctx context.Context, pairAddr common.Address) (*UniswapV2Reserves, error) { + result, err := u.Manager.CallContractMethodUnpackMap(ctx, u.network, UniswapV2Pair, pairAddr, "getReserves") + if err != nil { + return nil, fmt.Errorf("failed to get pair reserves: %w", err) + } + + return &UniswapV2Reserves{ + Reserve0: result["_reserve0"].(*big.Int), + Reserve1: result["_reserve1"].(*big.Int), + BlockTimestampLast: result["_blockTimestampLast"].(uint32), + }, nil +} + +func (u *UniswapV2) GetAmountOut(ctx context.Context, amountIn *big.Int, reserveIn *big.Int, reserveOut *big.Int) (*big.Int, error) { + result, err := u.Manager.CallContractMethod(ctx, u.network, UniswapV2Router, utils.ZeroAddress, "getAmountOut", amountIn, reserveIn, reserveOut) + if err != nil { + return nil, fmt.Errorf("failed to get amounts out: %w", err) + } + + amountOut, ok := result.(*big.Int) + if !ok { + return nil, fmt.Errorf("failed to assert amount result as []*big.Int: %v", result) + } + + return amountOut, nil +} + +func DefaultUniswapV2BindOptions() []*BindOptions { + return []*BindOptions{ + { + Networks: []utils.Network{utils.Ethereum, utils.AnvilNetwork}, + NetworkID: utils.EthereumNetworkID, + Name: "UniswapV2: Router", + Type: UniswapV2Router, + Address: common.HexToAddress("0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"), + ABI: `[{"inputs":[{"internalType":"address","name":"_factory","type":"address"},{"internalType":"address","name":"_WETH","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"amountADesired","type":"uint256"},{"internalType":"uint256","name":"amountBDesired","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountTokenDesired","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"reserveIn","type":"uint256"},{"internalType":"uint256","name":"reserveOut","type":"uint256"}],"name":"getAmountIn","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"reserveIn","type":"uint256"},{"internalType":"uint256","name":"reserveOut","type":"uint256"}],"name":"getAmountOut","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"}],"name":"getAmountsIn","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"}],"name":"getAmountsOut","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"reserveA","type":"uint256"},{"internalType":"uint256","name":"reserveB","type":"uint256"}],"name":"quote","outputs":[{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityETHSupportingFeeOnTransferTokens","outputs":[{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityETHWithPermit","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityETHWithPermitSupportingFeeOnTransferTokens","outputs":[{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityWithPermit","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapETHForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactETHForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactETHForTokensSupportingFeeOnTransferTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForETHSupportingFeeOnTransferTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForTokensSupportingFeeOnTransferTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapTokensForExactETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapTokensForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]`, + }, + { + Networks: []utils.Network{utils.Ethereum, utils.AnvilNetwork}, + NetworkID: utils.EthereumNetworkID, + Name: "UniswapV2: Factory Contract", + Type: UniswapV2Factory, + Address: common.HexToAddress("0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f"), + ABI: `[{"inputs":[{"internalType":"address","name":"_feeToSetter","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token0","type":"address"},{"indexed":true,"internalType":"address","name":"token1","type":"address"},{"indexed":false,"internalType":"address","name":"pair","type":"address"},{"indexed":false,"internalType":"uint256","name":"","type":"uint256"}],"name":"PairCreated","type":"event"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allPairs","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"allPairsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"name":"createPair","outputs":[{"internalType":"address","name":"pair","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"feeTo","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"feeToSetter","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"getPair","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_feeTo","type":"address"}],"name":"setFeeTo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_feeToSetter","type":"address"}],"name":"setFeeToSetter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]`, + }, + { + Networks: []utils.Network{utils.Ethereum, utils.AnvilNetwork}, + NetworkID: utils.EthereumNetworkID, + Name: "UniswapV2: Pair", + Type: UniswapV2Pair, + Address: common.HexToAddress("0x3356c9a8f40f8e9c1d192a4347a76d18243fabc5"), + ABI: `[{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0In","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1In","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount0Out","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1Out","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint112","name":"reserve0","type":"uint112"},{"indexed":false,"internalType":"uint112","name":"reserve1","type":"uint112"}],"name":"Sync","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"constant":true,"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MINIMUM_LIQUIDITY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"burn","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getReserves","outputs":[{"internalType":"uint112","name":"_reserve0","type":"uint112"},{"internalType":"uint112","name":"_reserve1","type":"uint112"},{"internalType":"uint32","name":"_blockTimestampLast","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_token0","type":"address"},{"internalType":"address","name":"_token1","type":"address"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"kLast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"price0CumulativeLast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"price1CumulativeLast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"skim","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"amount0Out","type":"uint256"},{"internalType":"uint256","name":"amount1Out","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swap","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"sync","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"token0","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"token1","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]`, + }, + } +} diff --git a/bytecode/log.go b/bytecode/log.go index 63134665..eaa0fd29 100644 --- a/bytecode/log.go +++ b/bytecode/log.go @@ -56,19 +56,23 @@ func DecodeLogFromAbi(log *types.Log, abiData []byte) (*Log, error) { return nil, fmt.Errorf("failed to unpack inputs into map: %s", err) } - decodedTopics := make([]Topic, len(log.Topics)) - for i, topic := range log.Topics { - if i == 0 { - continue + // Identify and decode indexed inputs + indexedInputs := make([]abi.Argument, 0) + for _, input := range event.Inputs { + if input.Indexed { + indexedInputs = append(indexedInputs, input) } + } - decodedTopic, err := decodeTopic(topic, event.Inputs[i-1]) + decodedTopics := make([]Topic, len(indexedInputs)) + for i, indexedInput := range indexedInputs { + decodedTopic, err := decodeTopic(log.Topics[i+1], indexedInput) if err != nil { return nil, fmt.Errorf("failed to decode topic: %s", err) } decodedTopics[i] = Topic{ - Name: event.Inputs[i-1].Name, + Name: indexedInput.Name, Value: decodedTopic, } } @@ -87,7 +91,7 @@ func DecodeLogFromAbi(log *types.Log, abiData []byte) (*Log, error) { Name: event.Name, Type: utils.LogEventType(strings.ToLower(event.Name)), Data: data, - Topics: decodedTopics[1:], // Exclude the first topic (event signature) + Topics: decodedTopics, // Exclude the first topic (event signature) } return toReturn, nil diff --git a/bytecode/transaction.go b/bytecode/transaction.go index c64624db..538277b6 100644 --- a/bytecode/transaction.go +++ b/bytecode/transaction.go @@ -63,7 +63,12 @@ func DecodeTransactionFromAbi(data []byte, abiData []byte) (*Transaction, error) Signature: method.String(), Name: method.Name, Method: method, - Type: utils.TransactionMethodType(strings.ToLower(method.Name)), + Type: func() utils.TransactionMethodType { + if len(method.Name) > 0 { + return utils.TransactionMethodType(strings.ToLower(method.Name)) + } + return utils.UnknownTransactionMethodType + }(), Inputs: inputsMap, }, nil } diff --git a/contracts/constructor.go b/contracts/constructor.go index 325cdc9f..5e05db5e 100644 --- a/contracts/constructor.go +++ b/contracts/constructor.go @@ -47,6 +47,7 @@ func (c *Contract) DiscoverConstructor(ctx context.Context) error { return fmt.Errorf("constructor data index out of range") } + fmt.Println(string(cAbi)) constructor, err := bytecode.DecodeConstructorFromAbi(adjustedData[constructorDataIndex:], constructorAbi) if err != nil { if !strings.Contains(err.Error(), "would go over slice boundary") { diff --git a/ir/variables.go b/ir/variables.go index 314e18b8..c5cd5b2b 100644 --- a/ir/variables.go +++ b/ir/variables.go @@ -1,6 +1,7 @@ package ir import ( + "github.com/unpackdev/solgo/utils" "strings" ast_pb "github.com/unpackdev/protos/dist/go/ast" @@ -127,6 +128,9 @@ func (b *Builder) processStateVariables(unit *ast.StateVariableDeclaration) *Sta // It could be that the name of the type name node is not set, but the type description string is. if variableNode.Type == "" { + if variableNode.TypeDescription == nil { + utils.DumpNodeWithExit(variableNode) + } variableNode.Type = variableNode.TypeDescription.TypeString } diff --git a/utils/networks.go b/utils/networks.go index 4c19610f..f41a1b51 100644 --- a/utils/networks.go +++ b/utils/networks.go @@ -83,6 +83,10 @@ func (n Network) String() string { return string(n) } +func (n Network) GetNetworkID() NetworkID { + return GetNetworkID(n) +} + func (n NetworkID) ToBig() *big.Int { return new(big.Int).SetUint64(uint64(n)) } @@ -131,6 +135,25 @@ func GetNetworkFromID(id NetworkID) (Network, error) { } } +func GetNetworkFromInt(id uint64) (Network, error) { + switch id { + case EthereumNetworkID.Uint64(): + return Ethereum, nil + case BscNetworkID.Uint64(): + return Bsc, nil + case PolygonNetworkID.Uint64(): + return Polygon, nil + case AvalancheNetworkID.Uint64(): + return Avalanche, nil + case ArbitrumNetworkID.Uint64(): + return Arbitrum, nil + case OptimismNetworkID.Uint64(): + return Optimism, nil + default: + return "", fmt.Errorf("unknown network ID '%d' provided", id) + } +} + func GetNetworkFromString(network string) (Network, error) { switch network { case "ethereum": diff --git a/utils/types.go b/utils/types.go index 8cd6aaa3..82da1c88 100644 --- a/utils/types.go +++ b/utils/types.go @@ -123,16 +123,21 @@ const ( UnknownTransactionMethodType TransactionMethodType = "unknown" ContractCreationType TransactionMethodType = "contract_creation" + ApproveMethodType TransactionMethodType = "approve" TransferMethodType TransactionMethodType = "transfer" + TransferFromMethodType TransactionMethodType = "transferfrom" + DepositMethodType TransactionMethodType = "deposit" NoSignatureMethodType TransactionMethodType = "no_signature" UnknownLogEventType LogEventType = "unknown" SwapLogEventType LogEventType = "swap" + TransferFromLogEventType LogEventType = "transferfrom" TransferLogEventType LogEventType = "transfer" DepositLogEventType LogEventType = "deposit" WithdrawLogEventType LogEventType = "withdraw" MintLogEventType LogEventType = "mint" BurnLogEventType LogEventType = "burn" + PairCreatedEventType LogEventType = "paircreated" NoSimulator SimulatorType = "no_simulator" AnvilSimulator SimulatorType = "anvil" diff --git a/utils/wei.go b/utils/wei.go new file mode 100644 index 00000000..2e71420e --- /dev/null +++ b/utils/wei.go @@ -0,0 +1,35 @@ +package utils + +import ( + "github.com/unpackdev/solgo/utils/entities" + "math/big" +) + +var Ether = big.NewInt(1e18) +var GWei = big.NewInt(1e9) + +// FromWei converts a balance in wei to Ether. +func FromWei(wei *big.Int, token *entities.Token) *entities.CurrencyAmount { + if wei == nil || token == nil { + return nil + } + return entities.FromRawAmount(token, wei) +} + +func ToOne(token *entities.Token) *entities.CurrencyAmount { + if token == nil { + return nil + } + + divisor := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(token.Decimals())), nil) + return entities.FromRawAmount(token, divisor) +} + +func ToMany(amount *big.Int, token *entities.Token) *entities.CurrencyAmount { + if amount == nil || token == nil { + return nil + } + + divisor := new(big.Int).Mul(amount, ToOne(token).Quotient()) + return entities.FromRawAmount(token, divisor) +} From bbaf95d9fe7642d8167b18fee1a568282a671b9d Mon Sep 17 00:00:00 2001 From: Nevio Date: Sun, 23 Jun 2024 13:50:19 +0200 Subject: [PATCH 03/11] Local to upstream --- accounts/account.go | 42 ++++++------ ast/reference.go | 4 ++ ast/type_name.go | 13 +--- bindings/manager.go | 106 ++++++++++++++++++++++++++++++ bytecode/log.go | 22 ++++--- contracts/chain.go | 18 +++-- contracts/contract.go | 6 +- envio/blocks.go | 3 + envio/client.go | 89 +++++++++++++++++++++++++ envio/creator.go | 64 ++++++++++++++++++ envio/errors.go | 7 ++ envio/logs.go | 1 + envio/options.go | 66 +++++++++++++++++++ envio/response.go | 32 +++++++++ envio/subscriber.go | 42 ++++++++++++ envio/transactions.go | 1 + go.mod | 35 ++++------ go.sum | 45 +++++-------- ir/standards.go | 4 ++ storage/storage.go | 4 +- utils/entities/currency_amount.go | 4 -- utils/math.go | 7 ++ utils/networks.go | 5 ++ 23 files changed, 517 insertions(+), 103 deletions(-) create mode 100644 envio/blocks.go create mode 100644 envio/client.go create mode 100644 envio/creator.go create mode 100644 envio/errors.go create mode 100644 envio/logs.go create mode 100644 envio/options.go create mode 100644 envio/response.go create mode 100644 envio/subscriber.go create mode 100644 envio/transactions.go create mode 100644 utils/math.go diff --git a/accounts/account.go b/accounts/account.go index 1bf44aad..15c4196d 100644 --- a/accounts/account.go +++ b/accounts/account.go @@ -4,20 +4,17 @@ import ( "context" "encoding/base64" "fmt" - "github.com/goccy/go-json" - "log" - "math/big" - "os" - "strings" - account "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" + "github.com/goccy/go-json" "github.com/unpackdev/solgo/clients" "github.com/unpackdev/solgo/utils" + "log" + "math/big" + "os" ) const ( @@ -112,21 +109,22 @@ func (a *Account) TransactOpts(client *clients.Client, amount *big.Int, simulate if !simulate { if a.Type == utils.SimpleAccountType { - privateKey, err := crypto.HexToECDSA(strings.TrimLeft(a.PrivateKey, "0x")) - if err != nil { - return nil, err - } - - auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(client.GetNetworkID())) - if err != nil { - return nil, err - } - - auth.Nonce = big.NewInt(int64(nonce)) - auth.GasPrice = gasPrice - auth.GasLimit = DEFAULT_GAS_LIMIT - auth.Value = amount - return auth, nil + /* privateKey, err := crypto.HexToECDSA(strings.TrimLeft(a.PrivateKey, "0x")) + if err != nil { + return nil, err + }*/ + + /* auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(client.GetNetworkID())) + if err != nil { + return nil, err + } + + auth.Nonce = big.NewInt(int64(nonce)) + auth.GasPrice = gasPrice + auth.GasLimit = DEFAULT_GAS_LIMIT + auth.Value = amount + return auth, nil*/ + return nil, nil } else if a.Type == utils.KeystoreAccountType { password, _ := a.DecodePassword() diff --git a/ast/reference.go b/ast/reference.go index b0669464..569b3c90 100644 --- a/ast/reference.go +++ b/ast/reference.go @@ -553,6 +553,10 @@ func (r *Resolver) byEnums(name string) (int64, *TypeDescription) { } func (r *Resolver) byStructs(name string) (int64, *TypeDescription) { + if strings.Contains(name, ".") { + name = strings.Split(name, ".")[1] + } + for _, node := range r.currentStructs { structNode := node.(*StructDefinition) if structNode.GetName() == name { diff --git a/ast/type_name.go b/ast/type_name.go index cb06167c..1b6bdbd6 100644 --- a/ast/type_name.go +++ b/ast/type_name.go @@ -62,10 +62,8 @@ func (t *TypeName) WithParentNode(p Node[NodeType]) { // SetReferenceDescriptor sets the reference descriptions of the TypeName node. func (t *TypeName) SetReferenceDescriptor(refId int64, refDesc *TypeDescription) bool { - if t.TypeDescription == nil { - t.ReferencedDeclaration = refId - t.TypeDescription = refDesc - } + t.ReferencedDeclaration = refId + t.TypeDescription = refDesc // Lets update the parent node as well in case that type description is not set... /* parentNodeId := t.GetSrc().GetParentIndex() @@ -550,11 +548,6 @@ func (t *TypeName) parseIdentifierPath(unit *SourceUnit[Node[ast_pb.SourceUnit]] t.TypeDescription = refTypeDescription } } - - /* if t.Id == 1787 { - fmt.Println("HERE I AM") - utils.DumpNodeWithExit(t) - }*/ } } @@ -990,7 +983,7 @@ func (td *TypeDescription) GetString() string { } // ToProto converts the TypeDescription instance to its corresponding protocol buffer representation. -func (td TypeDescription) ToProto() *ast_pb.TypeDescription { +func (td *TypeDescription) ToProto() *ast_pb.TypeDescription { return &ast_pb.TypeDescription{ TypeString: td.TypeString, TypeIdentifier: td.TypeIdentifier, diff --git a/bindings/manager.go b/bindings/manager.go index ae1ec912..5a0ba7d0 100644 --- a/bindings/manager.go +++ b/bindings/manager.go @@ -3,6 +3,7 @@ package bindings import ( "context" "fmt" + "math/big" "strings" "sync" @@ -192,6 +193,58 @@ func (m *Manager) CallContractMethod(ctx context.Context, network utils.Network, return unpackedResults, nil } +// CallContractMethodAtBlock executes a method call on a smart contract, handling the data packing, RPC call execution, +// and results unpacking. +func (m *Manager) CallContractMethodAtBlock(ctx context.Context, network utils.Network, blockNumber *big.Int, bindingType BindingType, toAddr common.Address, methodName string, params ...interface{}) (any, error) { + m.mu.RLock() + defer m.mu.RUnlock() + + binding, ok := m.bindings[network][bindingType] + if !ok { + return nil, fmt.Errorf("binding %s not found for network %s", bindingType, network) + } + + method, ok := binding.ABI.Methods[methodName] + if !ok { + return nil, fmt.Errorf("binding %s method %s not found in ABI", bindingType, methodName) + } + + data, err := method.Inputs.Pack(params...) + if err != nil { + return nil, err + } + + destinationAddr := toAddr + if destinationAddr == utils.ZeroAddress { + destinationAddr = binding.Address + } + + callMsg := ethereum.CallMsg{ + To: &destinationAddr, + Data: append(method.ID, data...), + } + + var result []byte + + client := m.clientPool.GetClientByGroup(network.String()) + if client == nil { + return nil, fmt.Errorf("client not found for network %s", network) + } + + result, err = client.CallContract(context.Background(), callMsg, blockNumber) + if err != nil { + return nil, fmt.Errorf("failed to call contract: %w", err) + } + + var unpackedResults any + err = binding.ABI.UnpackIntoInterface(&unpackedResults, methodName, result) + if err != nil { + return nil, fmt.Errorf("failed to unpack results: %w", err) + } + + return unpackedResults, nil +} + // CallContractMethodUnpackMap executes a contract method call and unpacks the results into a map, providing // a flexible interface for handling contract outputs. func (m *Manager) CallContractMethodUnpackMap(ctx context.Context, network utils.Network, bindingType BindingType, toAddr common.Address, methodName string, params ...interface{}) (map[string]any, error) { @@ -243,3 +296,56 @@ func (m *Manager) CallContractMethodUnpackMap(ctx context.Context, network utils return unpackedResults, nil } + + +// CallContractMethodUnpackMap executes a contract method call and unpacks the results into a map, providing +// a flexible interface for handling contract outputs. +func (m *Manager) CallContractMethodUnpackMapAtBlock(ctx context.Context, network utils.Network, blockNumber *big.Int, bindingType BindingType, toAddr common.Address, methodName string, params ...interface{}) (map[string]any, error) { + m.mu.RLock() + defer m.mu.RUnlock() + + binding, ok := m.bindings[network][bindingType] + if !ok { + return nil, fmt.Errorf("binding %s not found for network %s", bindingType, network) + } + + method, ok := binding.ABI.Methods[methodName] + if !ok { + return nil, fmt.Errorf("binding %s method %s not found in ABI", bindingType, methodName) + } + + data, err := method.Inputs.Pack(params...) + if err != nil { + return nil, err + } + + destinationAddr := toAddr + if destinationAddr == utils.ZeroAddress { + destinationAddr = binding.Address + } + + callMsg := ethereum.CallMsg{ + To: &destinationAddr, + Data: append(method.ID, data...), + } + + var result []byte + + client := m.clientPool.GetClientByGroup(network.String()) + if client == nil { + return nil, fmt.Errorf("client not found for network %s", network) + } + + result, err = client.CallContract(context.Background(), callMsg, blockNumber) + if err != nil { + return nil, fmt.Errorf("failed to call contract: %w", err) + } + + unpackedResults := map[string]any{} + err = binding.ABI.UnpackIntoMap(unpackedResults, methodName, result) + if err != nil { + return nil, fmt.Errorf("failed to unpack results: %w", err) + } + + return unpackedResults, nil +} diff --git a/bytecode/log.go b/bytecode/log.go index eaa0fd29..0cba743c 100644 --- a/bytecode/log.go +++ b/bytecode/log.go @@ -64,17 +64,21 @@ func DecodeLogFromAbi(log *types.Log, abiData []byte) (*Log, error) { } } + if len(log.Topics) < len(indexedInputs)+1 { + return nil, fmt.Errorf("insufficient number of topics in log.Topics") + } + decodedTopics := make([]Topic, len(indexedInputs)) for i, indexedInput := range indexedInputs { - decodedTopic, err := decodeTopic(log.Topics[i+1], indexedInput) - if err != nil { - return nil, fmt.Errorf("failed to decode topic: %s", err) - } - - decodedTopics[i] = Topic{ - Name: indexedInput.Name, - Value: decodedTopic, - } + decodedTopic, err := decodeTopic(log.Topics[i+1], indexedInput) + if err != nil { + return nil, fmt.Errorf("failed to decode topic: %s", err) + } + + decodedTopics[i] = Topic{ + Name: indexedInput.Name, + Value: decodedTopic, + } } eventAbi, err := utils.EventToABI(event) diff --git a/contracts/chain.go b/contracts/chain.go index aead2649..fba80c41 100644 --- a/contracts/chain.go +++ b/contracts/chain.go @@ -3,9 +3,10 @@ package contracts import ( "context" "fmt" - "github.com/unpackdev/solgo/bindings" - + hypersyncgo "github.com/enviodev/hypersync-client-go" "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + "github.com/unpackdev/solgo/bindings" "github.com/unpackdev/solgo/utils" ) @@ -30,6 +31,15 @@ func (c *Contract) DiscoverChainInfo(ctx context.Context, otsLookup bool) error var txHash common.Hash if info == nil || info.CreationHash == utils.ZeroHash { + cInfo, err := c.hypersync.GetContractCreator(ctx, c.network.GetToHyperSyncNetworkID(), c.addr) + if err != nil && !errors.Is(err, hypersyncgo.ErrContractNotFound) { + return errors.Wrap(err, "failed to get contract creator transaction information") + } else if cInfo != nil { + txHash = cInfo.Hash + } + } + + if txHash == utils.ZeroHash || info == nil || info.CreationHash == utils.ZeroHash { // Prior to continuing with the unpacking of the contract, we want to make sure that we can reach properly // contract transaction and associated creation block. If we can't, we're not going to unpack it. cInfo, err := c.etherscan.QueryContractCreationTx(ctx, c.addr) @@ -56,11 +66,11 @@ func (c *Contract) DiscoverChainInfo(ctx context.Context, otsLookup bool) error } c.descriptor.Receipt = receipt - block, err := c.client.BlockByNumber(ctx, receipt.BlockNumber) + block, err := c.client.HeaderByNumber(ctx, receipt.BlockNumber) if err != nil { return fmt.Errorf("failed to get block by number: %s", err) } - c.descriptor.Block = block.Header() + c.descriptor.Block = block if len(c.descriptor.ExecutionBytecode) < 1 { c.descriptor.ExecutionBytecode = c.descriptor.Transaction.Data() diff --git a/contracts/contract.go b/contracts/contract.go index 81784416..e324b68a 100644 --- a/contracts/contract.go +++ b/contracts/contract.go @@ -3,8 +3,8 @@ package contracts import ( "context" "fmt" - "github.com/0x19/solc-switch" + hypersyncgo "github.com/enviodev/hypersync-client-go" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/unpackdev/solgo/bindings" @@ -33,6 +33,7 @@ type Metadata struct { type Contract struct { ctx context.Context clientPool *clients.ClientPool + hypersync *hypersyncgo.Client client *clients.Client addr common.Address network utils.Network @@ -50,7 +51,7 @@ type Contract struct { // NewContract creates a new instance of Contract for a given Ethereum address and network. // It initializes the contract's context, metadata, and associated blockchain clients. // The function validates the contract's existence and its bytecode before creation. -func NewContract(ctx context.Context, network utils.Network, clientPool *clients.ClientPool, stor *storage.Storage, bqp *bitquery.Provider, etherscan *etherscan.Provider, compiler *solc.Solc, bindManager *bindings.Manager, ipfsProvider metadata.Provider, addr common.Address) (*Contract, error) { +func NewContract(ctx context.Context, network utils.Network, clientPool *clients.ClientPool, hypersync *hypersyncgo.Client, stor *storage.Storage, bqp *bitquery.Provider, etherscan *etherscan.Provider, compiler *solc.Solc, bindManager *bindings.Manager, ipfsProvider metadata.Provider, addr common.Address) (*Contract, error) { if clientPool == nil { return nil, fmt.Errorf("client pool is nil") } @@ -83,6 +84,7 @@ func NewContract(ctx context.Context, network utils.Network, clientPool *clients toReturn := &Contract{ ctx: ctx, network: network, + hypersync: hypersync, clientPool: clientPool, client: client, addr: addr, diff --git a/envio/blocks.go b/envio/blocks.go new file mode 100644 index 00000000..9b254f01 --- /dev/null +++ b/envio/blocks.go @@ -0,0 +1,3 @@ +package envio + +type Block struct{} diff --git a/envio/client.go b/envio/client.go new file mode 100644 index 00000000..6c9c620e --- /dev/null +++ b/envio/client.go @@ -0,0 +1,89 @@ +package envio + +import ( + "context" + "fmt" + "github.com/goccy/go-json" + "github.com/pkg/errors" + "github.com/unpackdev/solgo/utils" + "io" + "net" + "net/http" + "strings" + "time" +) + +type Client struct { + ctx context.Context + opts *Options + client *http.Client +} + +func NewClient(ctx context.Context, opts *Options) (*Client, error) { + client := &http.Client{ + Timeout: 2 * time.Minute, + Transport: &http.Transport{ + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + }, + } + return &Client{ + ctx: ctx, + opts: opts, + client: client, + }, nil +} + +func (c *Client) GetQueryUrlFromNode(node Node) string { + return strings.Join([]string{node.Endpoint, "query"}, "/") +} + +func Do[R any, T any](ctx context.Context, c *Client, method string, networkId utils.NetworkID, payload R) (*T, error) { + node, nodeFound := c.opts.GetNodeByNetworkId(networkId) + if !nodeFound { + return nil, fmt.Errorf("requested envio network not found: networkId: %s", networkId) + } + + nodeUrl := c.GetQueryUrlFromNode(*node) + + reqPayload, err := json.Marshal(payload) + if err != nil { + return nil, errors.Wrap(err, "failed to marshal envio payload") + } + + req, err := http.NewRequestWithContext(ctx, method, nodeUrl, strings.NewReader(string(reqPayload))) + if err != nil { + return nil, errors.Wrap(err, "failed to create new request") + } + + req.Header.Set("Content-Type", "application/json") + + resp, err := c.client.Do(req) + if err != nil { + return nil, errors.Wrap(err, "failed to perform request") + } + defer resp.Body.Close() + + responseData, err := io.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "failed to read response body") + } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected status code: %d, response: %s", resp.StatusCode, string(responseData)) + } + + var result T + err = json.Unmarshal(responseData, &result) + if err != nil { + return nil, errors.Wrap(err, "failed to unmarshal response body") + } + + return &result, nil +} diff --git a/envio/creator.go b/envio/creator.go new file mode 100644 index 00000000..ba76c635 --- /dev/null +++ b/envio/creator.go @@ -0,0 +1,64 @@ +package envio + +import ( + "context" + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + "github.com/unpackdev/solgo/utils" + "net/http" +) + +// ContractCreatorFieldSelection represents the fields to be selected from the transaction response. +type ContractCreatorFieldSelection struct { + Transaction []string `json:"transaction"` +} + +// ContractCreatorTransaction represents the transaction data containing contract addresses. +type ContractCreatorTransaction struct { + ContractAddress []common.Address `json:"contract_address"` +} + +// ContractCreatorRequest represents the request structure for fetching contract creator details. +type ContractCreatorRequest struct { + FromBlock int `json:"from_block"` + Transactions []ContractCreatorTransaction `json:"transactions"` + FieldSelection ContractCreatorFieldSelection `json:"field_selection"` +} + +// GetContractCreator fetches the transaction details of the creator of a specified contract address. +// +// This function sends a POST request to the Envio API to retrieve information about the contract creator. +// It takes the context, network ID, and contract address as parameters and returns the transaction details. +// +// Parameters: +// - ctx: The context for managing the request lifecycle. +// - networkId: The ID of the network where the contract is deployed. +// - addr: The contract address for which the creator's transaction details are to be fetched. +// +// Returns: +// - *Transaction: The transaction details of the contract creator, if found. +// - error: An error if the request fails or the contract is not found. +func (c *Client) GetContractCreator(ctx context.Context, networkId utils.NetworkID, addr common.Address) (*Transaction, error) { + request := ContractCreatorRequest{ + FromBlock: 0, + Transactions: []ContractCreatorTransaction{ + { + ContractAddress: []common.Address{addr}, + }, + }, + FieldSelection: ContractCreatorFieldSelection{ + Transaction: []string{"hash", "block_number"}, + }, + } + + response, err := Do[ContractCreatorRequest, Response](ctx, c, http.MethodPost, networkId, request) + if err != nil { + return nil, errors.Wrap(err, "failed to get contract creator from envio") + } + + if len(response.Data) == 0 { + return nil, ErrContractNotFound + } + + return response.Data[0].Transactions[0], nil +} diff --git a/envio/errors.go b/envio/errors.go new file mode 100644 index 00000000..5774e6d9 --- /dev/null +++ b/envio/errors.go @@ -0,0 +1,7 @@ +package envio + +import "errors" + +var ( + ErrContractNotFound = errors.New("contract not found") +) diff --git a/envio/logs.go b/envio/logs.go new file mode 100644 index 00000000..47c3b46d --- /dev/null +++ b/envio/logs.go @@ -0,0 +1 @@ +package envio diff --git a/envio/options.go b/envio/options.go new file mode 100644 index 00000000..edd99b06 --- /dev/null +++ b/envio/options.go @@ -0,0 +1,66 @@ +package envio + +import "github.com/unpackdev/solgo/utils" + +// Options represents the configuration options for network nodes. +type Options struct { + // Nodes is a slice of Node representing the network nodes. + Blockchains []Node `mapstructure:"blockchains" yaml:"blockchains" json:"blockchains"` +} + +// GetBlockchains returns the slice of network blockchains nodes from the Options. +func (o *Options) GetBlockchains() []Node { + return o.Blockchains +} + +func (o *Options) GetNodeByNetworkId(networkId utils.NetworkID) (*Node, bool) { + for _, node := range o.Blockchains { + if node.NetworkId == int(networkId.Uint64()) { + return &node, true + } + } + return nil, false +} + +// Node represents the configuration and details of a network node. +type Node struct { + // Group represents the group name of the node. + Group string `mapstructure:"group" yaml:"group" json:"group"` + + // Type represents the type of the node. + Type string `mapstructure:"type" yaml:"type" json:"type"` + + // NetworkId represents the network ID of the node. + NetworkId int `mapstructure:"networkId" yaml:"networkId" json:"networkId"` + + // Endpoint represents the network endpoint of the node. + Endpoint string `mapstructure:"endpoint" yaml:"endpoint" json:"endpoint"` + + // ConcurrentClients represents the number of concurrent clients for the node. + ConcurrentClients int `mapstructure:"concurrentClients" yaml:"concurrentClients" json:"concurrentClients"` +} + +// GetGroup returns the group name of the node. +func (n *Node) GetGroup() string { + return n.Group +} + +// GetType returns the type of the node. +func (n *Node) GetType() string { + return n.Type +} + +// GetNetworkID returns the network ID of the node. +func (n *Node) GetNetworkID() int64 { + return int64(n.NetworkId) +} + +// GetEndpoint returns the network endpoint of the node. +func (n *Node) GetEndpoint() string { + return n.Endpoint +} + +// GetConcurrentClientsNumber returns the number of concurrent clients for the node. +func (n *Node) GetConcurrentClientsNumber() int { + return n.ConcurrentClients +} diff --git a/envio/response.go b/envio/response.go new file mode 100644 index 00000000..95d6ff96 --- /dev/null +++ b/envio/response.go @@ -0,0 +1,32 @@ +package envio + +import ( + "github.com/ethereum/go-ethereum/common" + "math/big" +) + +// Structs to represent the JSON structure +type Response struct { + Data []Data `json:"data"` + ArchiveHeight int `json:"archive_height"` + NextBlock int `json:"next_block"` + TotalExecutionTime int `json:"total_execution_time"` + RollbackGuard RollbackGuard `json:"rollback_guard"` +} + +type RollbackGuard struct { + BlockNumber int `json:"block_number"` + Timestamp int64 `json:"timestamp"` + Hash string `json:"hash"` + FirstBlockNumber int `json:"first_block_number"` + FirstParentHash string `json:"first_parent_hash"` +} + +type Transaction struct { + BlockNumber *big.Int `json:"block_number"` + Hash common.Hash `json:"hash"` +} + +type Data struct { + Transactions []*Transaction `json:"transactions"` +} diff --git a/envio/subscriber.go b/envio/subscriber.go new file mode 100644 index 00000000..b7c38bf0 --- /dev/null +++ b/envio/subscriber.go @@ -0,0 +1,42 @@ +package envio + +import ( + "context" + "math/big" + "sync" +) + +type Subscriber struct { + ctx context.Context + client *Client + startBlockNumber *big.Int + endBlockNumber *big.Int + mu sync.Mutex + ch chan *Block +} + +func NewBlockSubscriber(ctx context.Context, client *Client, startBlockNumber, endBlockNumber *big.Int) (*Subscriber, error) { + toReturn := &Subscriber{ + ctx: ctx, + client: client, + startBlockNumber: startBlockNumber, + endBlockNumber: endBlockNumber, + mu: sync.Mutex{}, + ch: make(chan *Block), + } + + return toReturn, nil +} + +func (s *Subscriber) Subscribe() error { + + return nil +} + +func (s *Subscriber) UnSubscribe() error { + return nil +} + +func (s *Subscriber) Ch() <-chan *Block { + return s.ch +} diff --git a/envio/transactions.go b/envio/transactions.go new file mode 100644 index 00000000..47c3b46d --- /dev/null +++ b/envio/transactions.go @@ -0,0 +1 @@ +package envio diff --git a/go.mod b/go.mod index 6e6855d5..522bd1d8 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,13 @@ module github.com/unpackdev/solgo -go 1.22 - -toolchain go1.22.0 +go 1.22.4 require ( github.com/0x19/solc-switch v1.0.4 github.com/antlr4-go/antlr/v4 v4.13.0 github.com/cncf/xds/go v0.0.0-20240312170511-ee0267137e25 - github.com/ethereum/go-ethereum v1.13.15 + github.com/enviodev/hypersync-client-go v0.0.0-00010101000000-000000000000 + github.com/ethereum/go-ethereum v1.14.5 github.com/fxamacker/cbor/v2 v2.6.0 github.com/goccy/go-json v0.10.2 github.com/golang/protobuf v1.5.4 @@ -23,30 +22,23 @@ require ( github.com/stretchr/testify v1.8.4 github.com/unpackdev/protos v0.3.5 go.uber.org/zap v1.26.0 - golang.org/x/crypto v0.21.0 - golang.org/x/sync v0.6.0 + golang.org/x/crypto v0.22.0 + golang.org/x/sync v0.7.0 google.golang.org/protobuf v1.33.0 ) -//replace github.com/antlr4-go/antlr/v4 => github.com/unpackdev/antlr4-go/v4 v4.13.0 -//replace github.com/unpackdev/protos => ../protos -//replace github.com/unpackdev/solgo => ../solgo-orig - require ( github.com/DataDog/zstd v1.5.5 // indirect - github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/VictoriaMetrics/fastcache v1.12.2 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.3 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/cockroachdb/pebble v1.1.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect - github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect @@ -67,7 +59,6 @@ require ( github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-flow-metrics v0.1.0 // indirect github.com/libp2p/go-libp2p v0.28.2 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -102,20 +93,18 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect - golang.org/x/mod v0.16.0 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/sys v0.18.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.19.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 // indirect google.golang.org/grpc v1.62.1 // indirect - gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.2.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) +replace github.com/enviodev/hypersync-client-go => ../hypersync-client-go + // https://github.com/ethereum/go-ethereum/issues/28285 -replace github.com/crate-crypto/go-kzg-4844 v1.0.0 => github.com/crate-crypto/go-kzg-4844 v0.7.0 +// replace github.com/crate-crypto/go-kzg-4844 v1.0.0 => github.com/crate-crypto/go-kzg-4844 v0.7.0 diff --git a/go.sum b/go.sum index a42ce879..46c0febf 100644 --- a/go.sum +++ b/go.sum @@ -2,11 +2,10 @@ github.com/0x19/solc-switch v1.0.4 h1:F7GiBg9kVyzp/N5l7P4aWg2Hb05Zn368vHE4e+F8Ho github.com/0x19/solc-switch v1.0.4/go.mod h1:3Hg7nE0J84D1rbYTNwe0bXK750XJZiBXHsYIjLjxlDA= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= -github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= @@ -25,8 +24,8 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.0.3 h1:SDlJ7bAm4ewvrmZtR0DaiYbQGd github.com/btcsuite/btcd/chaincfg/chainhash v1.0.3/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/cheekybits/is v0.0.0-20150225183255-68e9c0620927 h1:SKI1/fuSdodxmNNyVBR8d7X/HuLnRpvvFO0AgyQk764= github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= github.com/cncf/xds/go v0.0.0-20240312170511-ee0267137e25 h1:0WA3CLhwyvc3+Bz8ftwUDUg/Tj2UyfM3ld346AgtAhs= @@ -51,8 +50,8 @@ github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 h1:HVTnpeuv github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I= github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= -github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= -github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= +github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= +github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= 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= @@ -68,8 +67,10 @@ github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-ethereum v1.13.15 h1:U7sSGYGo4SPjP6iNIifNoyIAiNjrmQkz6EwQG+/EZWo= -github.com/ethereum/go-ethereum v1.13.15/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU= +github.com/ethereum/go-ethereum v1.14.5 h1:szuFzO1MhJmweXjoM5nSAeDvjNUH3vIQoMzzQnfvjpw= +github.com/ethereum/go-ethereum v1.14.5/go.mod h1:VEDGGhSxY7IEjn98hJRFXl/uFvpRgbIIf2PpXiyGGgc= +github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 h1:KrE8I4reeVvf7C1tm8elRjj4BdscTYzz/WAbYyf/JI4= +github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0/go.mod h1:D9AJLVXSyZQXJQVk8oh1EwjISE+sJTn2duYIZC0dy3w= github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -78,8 +79,6 @@ github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1t github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= -github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -95,7 +94,6 @@ github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOW github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -217,7 +215,6 @@ github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2 github.com/status-im/keycard-go v0.3.2 h1:YusIF/bHx6YZis8UTOJrpZFnTs4IkRBdmJXqdiXkpFE= github.com/status-im/keycard-go v0.3.2/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= @@ -249,29 +246,23 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= -golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= -golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 h1:oqta3O3AnlWbmIE3bFnWbu4bRxZjfbWCp0cKSuZh01E= google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7/go.mod h1:VQW3tUculP/D4B+xVCo+VgSq8As6wA9ZjHl//pmk+6s= google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 h1:8EeVk1VKMD+GD/neyEHGmz7pFblqPjHoi+PGQIlLx2s= diff --git a/ir/standards.go b/ir/standards.go index 58299cdf..9e72d08a 100644 --- a/ir/standards.go +++ b/ir/standards.go @@ -3,6 +3,7 @@ package ir import ( ir_pb "github.com/unpackdev/protos/dist/go/ir" "github.com/unpackdev/solgo/standards" + "github.com/unpackdev/solgo/utils" ) // Standard represents a specific Ethereum Improvement Proposal standard that a contract may adhere to. @@ -81,6 +82,9 @@ func (b *Builder) processEips(root *RootSourceUnit) { Type: "t_unknown", // Will fix this later on with upgrade of parser to support solidity 0.5+ }) } else { + if ret.GetTypeDescription() == nil { + utils.DumpNodeWithExit(function) + } outputs = append(outputs, standards.Output{ Type: ret.GetTypeDescription().GetString(), }) diff --git a/storage/storage.go b/storage/storage.go index 9eca1084..e310cce3 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -135,11 +135,11 @@ func (s *Storage) getStorageValueAt(ctx context.Context, contractAddress common. } if blockNumber == nil { - latestHeader, err := client.BlockByNumber(ctx, nil) + latestHeader, err := client.HeaderByNumber(ctx, nil) if err != nil { return blockNumber, nil, fmt.Errorf("failed to get latest block header: %v", err) } - blockNumber = latestHeader.Number() + blockNumber = latestHeader.Number } bigIntIndex := big.NewInt(slot) diff --git a/utils/entities/currency_amount.go b/utils/entities/currency_amount.go index 91997381..0481d55c 100644 --- a/utils/entities/currency_amount.go +++ b/utils/entities/currency_amount.go @@ -42,10 +42,6 @@ func (c *CurrencyAmount) UnmarshalJSON(data []byte) error { return err } c.Currency = &baseCurrency - - // If you have other types that implement Currency, you can - // include additional logic here to decide which type to unmarshal into. - // For example, you might look for specific fields that are unique to each type. } return nil diff --git a/utils/math.go b/utils/math.go new file mode 100644 index 00000000..799e3b95 --- /dev/null +++ b/utils/math.go @@ -0,0 +1,7 @@ +package utils + +import "math/big" + +func Pow(i, e *big.Int) *big.Int { + return new(big.Int).Exp(i, e, nil) +} diff --git a/utils/networks.go b/utils/networks.go index f41a1b51..9123897d 100644 --- a/utils/networks.go +++ b/utils/networks.go @@ -2,6 +2,7 @@ package utils import ( "fmt" + "github.com/enviodev/hypersync-client-go/pkg/utils" "math/big" ) @@ -87,6 +88,10 @@ func (n Network) GetNetworkID() NetworkID { return GetNetworkID(n) } +func (n Network) GetToHyperSyncNetworkID() utils.NetworkID { + return utils.NetworkID(n.GetNetworkID().Uint64()) +} + func (n NetworkID) ToBig() *big.Int { return new(big.Int).SetUint64(uint64(n)) } From 58f50400159951354390ad6d04bb212012474d6d Mon Sep 17 00:00:00 2001 From: Nevio Date: Thu, 18 Jul 2024 20:47:10 +0200 Subject: [PATCH 04/11] Yet another shity... --- ast/source_unit.go | 4 +- bindings/trace.go | 56 +++++++++++++++ bytecode/log.go | 10 ++- contracts/chain.go | 12 ++-- contracts/constructor.go | 1 - contracts/descriptor.go | 1 + contracts/source.go | 1 + envio/blocks.go | 3 - envio/client.go | 89 ------------------------ envio/creator.go | 64 ----------------- envio/errors.go | 7 -- envio/logs.go | 1 - envio/options.go | 66 ------------------ envio/response.go | 32 --------- envio/subscriber.go | 42 ----------- envio/transactions.go | 1 - go.mod | 17 ++++- go.sum | 61 ++++++++++++++-- ir/root.go | 2 +- ir/standards.go | 3 +- ir/variables.go | 3 +- providers/etherscan/contract.go | 2 +- providers/etherscan/transactions.go | 2 +- standards/standards.go | 11 +++ utils/networks.go | 104 +++++++++++++++++----------- utils/version.go | 71 ++++++++++++++++++- 26 files changed, 291 insertions(+), 375 deletions(-) delete mode 100644 envio/blocks.go delete mode 100644 envio/client.go delete mode 100644 envio/creator.go delete mode 100644 envio/errors.go delete mode 100644 envio/logs.go delete mode 100644 envio/options.go delete mode 100644 envio/response.go delete mode 100644 envio/subscriber.go delete mode 100644 envio/transactions.go diff --git a/ast/source_unit.go b/ast/source_unit.go index a2a33c72..1af0b800 100644 --- a/ast/source_unit.go +++ b/ast/source_unit.go @@ -62,7 +62,7 @@ func (s *SourceUnit[T]) SetAbsolutePathFromSources(sources *solgo.Sources) { } // Use the compiled regex for matching. - if !found && regex.MatchString(unit.GetContent()) { + if regex.MatchString(unit.GetContent()) { s.AbsolutePath = filepath.Base(filepath.Clean(unit.Path)) found = true break @@ -70,7 +70,7 @@ func (s *SourceUnit[T]) SetAbsolutePathFromSources(sources *solgo.Sources) { } if !found { - zap.L().Warn( + zap.L().Debug( "Could not set absolute path from sources as source unit was not found in sources", zap.String("name", s.Name), zap.String("pattern", pattern), diff --git a/bindings/trace.go b/bindings/trace.go index e1a751b8..cdae71e5 100644 --- a/bindings/trace.go +++ b/bindings/trace.go @@ -4,11 +4,67 @@ import ( "context" "fmt" "github.com/pkg/errors" + "math/big" "github.com/ethereum/go-ethereum/common" "github.com/unpackdev/solgo/utils" ) +type Action struct { + CallType string `json:"callType,omitempty"` + From string `json:"from"` + Gas string `json:"gas"` + Input string `json:"input"` + To string `json:"to"` + Value string `json:"value"` + // Fields specific to reward actions + Author string `json:"author,omitempty"` + RewardType string `json:"rewardType,omitempty"` +} + +type Result struct { + GasUsed string `json:"gasUsed"` + Output string `json:"output"` +} + +type BlockTrace struct { + Action Action `json:"action"` + BlockHash string `json:"blockHash"` + BlockNumber int `json:"blockNumber"` + Error string `json:"error"` + Result *Result `json:"result,omitempty"` + Subtraces int `json:"subtraces"` + TraceAddress []int `json:"traceAddress"` + TransactionHash string `json:"transactionHash,omitempty"` + TransactionPosition int64 `json:"transactionPosition,omitempty"` + Type string `json:"type"` +} + +func (m *Manager) TraceBlock(ctx context.Context, network utils.Network, number *big.Int) ([]BlockTrace, error) { + client := m.clientPool.GetClientByGroup(network.String()) + if client == nil { + return nil, fmt.Errorf("client not found for network %s", network) + } + + rpcClient := client.GetRpcClient() + var result []BlockTrace + var tmp interface{} + + // Convert number to hexadecimal string + numberHex := fmt.Sprintf("0x%x", number) + + if err := rpcClient.CallContext(ctx, &result, "trace_block", numberHex); err != nil { + if err := rpcClient.CallContext(ctx, &tmp, "trace_block", numberHex); err != nil { + fmt.Println(err) + return nil, errors.Wrap(err, "failed to execute trace_block") + } + //utils.DumpNodeWithExit(tmp) + return nil, errors.Wrap(err, "failed to execute trace_block") + } + + return result, nil +} + func (m *Manager) TraceCallMany(ctx context.Context, network utils.Network, sender common.Address, nonce int64) (*common.Hash, error) { client := m.clientPool.GetClientByGroup(network.String()) if client == nil { diff --git a/bytecode/log.go b/bytecode/log.go index 0cba743c..d29e673b 100644 --- a/bytecode/log.go +++ b/bytecode/log.go @@ -64,12 +64,9 @@ func DecodeLogFromAbi(log *types.Log, abiData []byte) (*Log, error) { } } - if len(log.Topics) < len(indexedInputs)+1 { - return nil, fmt.Errorf("insufficient number of topics in log.Topics") - } - decodedTopics := make([]Topic, len(indexedInputs)) - for i, indexedInput := range indexedInputs { + if len(log.Topics) >= len(indexedInputs)+1 { + for i, indexedInput := range indexedInputs { decodedTopic, err := decodeTopic(log.Topics[i+1], indexedInput) if err != nil { return nil, fmt.Errorf("failed to decode topic: %s", err) @@ -79,6 +76,7 @@ func DecodeLogFromAbi(log *types.Log, abiData []byte) (*Log, error) { Name: indexedInput.Name, Value: decodedTopic, } + } } eventAbi, err := utils.EventToABI(event) @@ -95,7 +93,7 @@ func DecodeLogFromAbi(log *types.Log, abiData []byte) (*Log, error) { Name: event.Name, Type: utils.LogEventType(strings.ToLower(event.Name)), Data: data, - Topics: decodedTopics, // Exclude the first topic (event signature) + Topics: decodedTopics, // Include only the decoded topics } return toReturn, nil diff --git a/contracts/chain.go b/contracts/chain.go index fba80c41..c6bb56c7 100644 --- a/contracts/chain.go +++ b/contracts/chain.go @@ -3,9 +3,7 @@ package contracts import ( "context" "fmt" - hypersyncgo "github.com/enviodev/hypersync-client-go" "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" "github.com/unpackdev/solgo/bindings" "github.com/unpackdev/solgo/utils" ) @@ -30,16 +28,16 @@ func (c *Contract) DiscoverChainInfo(ctx context.Context, otsLookup bool) error var txHash common.Hash - if info == nil || info.CreationHash == utils.ZeroHash { - cInfo, err := c.hypersync.GetContractCreator(ctx, c.network.GetToHyperSyncNetworkID(), c.addr) - if err != nil && !errors.Is(err, hypersyncgo.ErrContractNotFound) { + /* if info == nil || info.CreationHash == utils.ZeroHash { + cInfo, err := c.hypersync.GetContractCreator(ctx, c.addr) + if err != nil && !errors.Is(err, errorshs.ErrContractNotFound) { return errors.Wrap(err, "failed to get contract creator transaction information") } else if cInfo != nil { txHash = cInfo.Hash } - } + }*/ - if txHash == utils.ZeroHash || info == nil || info.CreationHash == utils.ZeroHash { + if info == nil || info.CreationHash == utils.ZeroHash { // Prior to continuing with the unpacking of the contract, we want to make sure that we can reach properly // contract transaction and associated creation block. If we can't, we're not going to unpack it. cInfo, err := c.etherscan.QueryContractCreationTx(ctx, c.addr) diff --git a/contracts/constructor.go b/contracts/constructor.go index 5e05db5e..325cdc9f 100644 --- a/contracts/constructor.go +++ b/contracts/constructor.go @@ -47,7 +47,6 @@ func (c *Contract) DiscoverConstructor(ctx context.Context) error { return fmt.Errorf("constructor data index out of range") } - fmt.Println(string(cAbi)) constructor, err := bytecode.DecodeConstructorFromAbi(adjustedData[constructorDataIndex:], constructorAbi) if err != nil { if !strings.Contains(err.Error(), "would go over slice boundary") { diff --git a/contracts/descriptor.go b/contracts/descriptor.go index 46ba31e8..db6b9d9d 100644 --- a/contracts/descriptor.go +++ b/contracts/descriptor.go @@ -28,6 +28,7 @@ type Descriptor struct { Receipt *types.Receipt `json:"receipt,omitempty"` ABI string `json:"abi,omitempty"` Name string `json:"name,omitempty"` + UnitName string `json:"unitName,omitempty"` License string `json:"license,omitempty"` SolgoVersion string `json:"solgo_version,omitempty"` CompilerVersion string `json:"compiler_version,omitempty"` diff --git a/contracts/source.go b/contracts/source.go index dab56d44..fdcfa3d6 100644 --- a/contracts/source.go +++ b/contracts/source.go @@ -120,6 +120,7 @@ func (c *Contract) DiscoverSourceCode(ctx context.Context) error { } c.descriptor.Name = response.Name + c.descriptor.UnitName = response.Name c.descriptor.CompilerVersion = c.descriptor.SourcesRaw.CompilerVersion c.descriptor.Optimized = optimized c.descriptor.OptimizationRuns = optimizationRuns diff --git a/envio/blocks.go b/envio/blocks.go deleted file mode 100644 index 9b254f01..00000000 --- a/envio/blocks.go +++ /dev/null @@ -1,3 +0,0 @@ -package envio - -type Block struct{} diff --git a/envio/client.go b/envio/client.go deleted file mode 100644 index 6c9c620e..00000000 --- a/envio/client.go +++ /dev/null @@ -1,89 +0,0 @@ -package envio - -import ( - "context" - "fmt" - "github.com/goccy/go-json" - "github.com/pkg/errors" - "github.com/unpackdev/solgo/utils" - "io" - "net" - "net/http" - "strings" - "time" -) - -type Client struct { - ctx context.Context - opts *Options - client *http.Client -} - -func NewClient(ctx context.Context, opts *Options) (*Client, error) { - client := &http.Client{ - Timeout: 2 * time.Minute, - Transport: &http.Transport{ - DialContext: (&net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, - }).DialContext, - MaxIdleConns: 100, - IdleConnTimeout: 90 * time.Second, - TLSHandshakeTimeout: 10 * time.Second, - ExpectContinueTimeout: 1 * time.Second, - }, - } - return &Client{ - ctx: ctx, - opts: opts, - client: client, - }, nil -} - -func (c *Client) GetQueryUrlFromNode(node Node) string { - return strings.Join([]string{node.Endpoint, "query"}, "/") -} - -func Do[R any, T any](ctx context.Context, c *Client, method string, networkId utils.NetworkID, payload R) (*T, error) { - node, nodeFound := c.opts.GetNodeByNetworkId(networkId) - if !nodeFound { - return nil, fmt.Errorf("requested envio network not found: networkId: %s", networkId) - } - - nodeUrl := c.GetQueryUrlFromNode(*node) - - reqPayload, err := json.Marshal(payload) - if err != nil { - return nil, errors.Wrap(err, "failed to marshal envio payload") - } - - req, err := http.NewRequestWithContext(ctx, method, nodeUrl, strings.NewReader(string(reqPayload))) - if err != nil { - return nil, errors.Wrap(err, "failed to create new request") - } - - req.Header.Set("Content-Type", "application/json") - - resp, err := c.client.Do(req) - if err != nil { - return nil, errors.Wrap(err, "failed to perform request") - } - defer resp.Body.Close() - - responseData, err := io.ReadAll(resp.Body) - if err != nil { - return nil, errors.Wrap(err, "failed to read response body") - } - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("unexpected status code: %d, response: %s", resp.StatusCode, string(responseData)) - } - - var result T - err = json.Unmarshal(responseData, &result) - if err != nil { - return nil, errors.Wrap(err, "failed to unmarshal response body") - } - - return &result, nil -} diff --git a/envio/creator.go b/envio/creator.go deleted file mode 100644 index ba76c635..00000000 --- a/envio/creator.go +++ /dev/null @@ -1,64 +0,0 @@ -package envio - -import ( - "context" - "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" - "github.com/unpackdev/solgo/utils" - "net/http" -) - -// ContractCreatorFieldSelection represents the fields to be selected from the transaction response. -type ContractCreatorFieldSelection struct { - Transaction []string `json:"transaction"` -} - -// ContractCreatorTransaction represents the transaction data containing contract addresses. -type ContractCreatorTransaction struct { - ContractAddress []common.Address `json:"contract_address"` -} - -// ContractCreatorRequest represents the request structure for fetching contract creator details. -type ContractCreatorRequest struct { - FromBlock int `json:"from_block"` - Transactions []ContractCreatorTransaction `json:"transactions"` - FieldSelection ContractCreatorFieldSelection `json:"field_selection"` -} - -// GetContractCreator fetches the transaction details of the creator of a specified contract address. -// -// This function sends a POST request to the Envio API to retrieve information about the contract creator. -// It takes the context, network ID, and contract address as parameters and returns the transaction details. -// -// Parameters: -// - ctx: The context for managing the request lifecycle. -// - networkId: The ID of the network where the contract is deployed. -// - addr: The contract address for which the creator's transaction details are to be fetched. -// -// Returns: -// - *Transaction: The transaction details of the contract creator, if found. -// - error: An error if the request fails or the contract is not found. -func (c *Client) GetContractCreator(ctx context.Context, networkId utils.NetworkID, addr common.Address) (*Transaction, error) { - request := ContractCreatorRequest{ - FromBlock: 0, - Transactions: []ContractCreatorTransaction{ - { - ContractAddress: []common.Address{addr}, - }, - }, - FieldSelection: ContractCreatorFieldSelection{ - Transaction: []string{"hash", "block_number"}, - }, - } - - response, err := Do[ContractCreatorRequest, Response](ctx, c, http.MethodPost, networkId, request) - if err != nil { - return nil, errors.Wrap(err, "failed to get contract creator from envio") - } - - if len(response.Data) == 0 { - return nil, ErrContractNotFound - } - - return response.Data[0].Transactions[0], nil -} diff --git a/envio/errors.go b/envio/errors.go deleted file mode 100644 index 5774e6d9..00000000 --- a/envio/errors.go +++ /dev/null @@ -1,7 +0,0 @@ -package envio - -import "errors" - -var ( - ErrContractNotFound = errors.New("contract not found") -) diff --git a/envio/logs.go b/envio/logs.go deleted file mode 100644 index 47c3b46d..00000000 --- a/envio/logs.go +++ /dev/null @@ -1 +0,0 @@ -package envio diff --git a/envio/options.go b/envio/options.go deleted file mode 100644 index edd99b06..00000000 --- a/envio/options.go +++ /dev/null @@ -1,66 +0,0 @@ -package envio - -import "github.com/unpackdev/solgo/utils" - -// Options represents the configuration options for network nodes. -type Options struct { - // Nodes is a slice of Node representing the network nodes. - Blockchains []Node `mapstructure:"blockchains" yaml:"blockchains" json:"blockchains"` -} - -// GetBlockchains returns the slice of network blockchains nodes from the Options. -func (o *Options) GetBlockchains() []Node { - return o.Blockchains -} - -func (o *Options) GetNodeByNetworkId(networkId utils.NetworkID) (*Node, bool) { - for _, node := range o.Blockchains { - if node.NetworkId == int(networkId.Uint64()) { - return &node, true - } - } - return nil, false -} - -// Node represents the configuration and details of a network node. -type Node struct { - // Group represents the group name of the node. - Group string `mapstructure:"group" yaml:"group" json:"group"` - - // Type represents the type of the node. - Type string `mapstructure:"type" yaml:"type" json:"type"` - - // NetworkId represents the network ID of the node. - NetworkId int `mapstructure:"networkId" yaml:"networkId" json:"networkId"` - - // Endpoint represents the network endpoint of the node. - Endpoint string `mapstructure:"endpoint" yaml:"endpoint" json:"endpoint"` - - // ConcurrentClients represents the number of concurrent clients for the node. - ConcurrentClients int `mapstructure:"concurrentClients" yaml:"concurrentClients" json:"concurrentClients"` -} - -// GetGroup returns the group name of the node. -func (n *Node) GetGroup() string { - return n.Group -} - -// GetType returns the type of the node. -func (n *Node) GetType() string { - return n.Type -} - -// GetNetworkID returns the network ID of the node. -func (n *Node) GetNetworkID() int64 { - return int64(n.NetworkId) -} - -// GetEndpoint returns the network endpoint of the node. -func (n *Node) GetEndpoint() string { - return n.Endpoint -} - -// GetConcurrentClientsNumber returns the number of concurrent clients for the node. -func (n *Node) GetConcurrentClientsNumber() int { - return n.ConcurrentClients -} diff --git a/envio/response.go b/envio/response.go deleted file mode 100644 index 95d6ff96..00000000 --- a/envio/response.go +++ /dev/null @@ -1,32 +0,0 @@ -package envio - -import ( - "github.com/ethereum/go-ethereum/common" - "math/big" -) - -// Structs to represent the JSON structure -type Response struct { - Data []Data `json:"data"` - ArchiveHeight int `json:"archive_height"` - NextBlock int `json:"next_block"` - TotalExecutionTime int `json:"total_execution_time"` - RollbackGuard RollbackGuard `json:"rollback_guard"` -} - -type RollbackGuard struct { - BlockNumber int `json:"block_number"` - Timestamp int64 `json:"timestamp"` - Hash string `json:"hash"` - FirstBlockNumber int `json:"first_block_number"` - FirstParentHash string `json:"first_parent_hash"` -} - -type Transaction struct { - BlockNumber *big.Int `json:"block_number"` - Hash common.Hash `json:"hash"` -} - -type Data struct { - Transactions []*Transaction `json:"transactions"` -} diff --git a/envio/subscriber.go b/envio/subscriber.go deleted file mode 100644 index b7c38bf0..00000000 --- a/envio/subscriber.go +++ /dev/null @@ -1,42 +0,0 @@ -package envio - -import ( - "context" - "math/big" - "sync" -) - -type Subscriber struct { - ctx context.Context - client *Client - startBlockNumber *big.Int - endBlockNumber *big.Int - mu sync.Mutex - ch chan *Block -} - -func NewBlockSubscriber(ctx context.Context, client *Client, startBlockNumber, endBlockNumber *big.Int) (*Subscriber, error) { - toReturn := &Subscriber{ - ctx: ctx, - client: client, - startBlockNumber: startBlockNumber, - endBlockNumber: endBlockNumber, - mu: sync.Mutex{}, - ch: make(chan *Block), - } - - return toReturn, nil -} - -func (s *Subscriber) Subscribe() error { - - return nil -} - -func (s *Subscriber) UnSubscribe() error { - return nil -} - -func (s *Subscriber) Ch() <-chan *Block { - return s.ch -} diff --git a/envio/transactions.go b/envio/transactions.go deleted file mode 100644 index 47c3b46d..00000000 --- a/envio/transactions.go +++ /dev/null @@ -1 +0,0 @@ -package envio diff --git a/go.mod b/go.mod index 522bd1d8..c76016bc 100644 --- a/go.mod +++ b/go.mod @@ -21,20 +21,25 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/stretchr/testify v1.8.4 github.com/unpackdev/protos v0.3.5 - go.uber.org/zap v1.26.0 + go.uber.org/zap v1.27.0 golang.org/x/crypto v0.22.0 golang.org/x/sync v0.7.0 google.golang.org/protobuf v1.33.0 ) require ( + capnproto.org/go/capnp/v3 v3.0.1-alpha.1 // indirect github.com/DataDog/zstd v1.5.5 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/andybalholm/brotli v1.0.5 // indirect + github.com/apache/arrow/go/v10 v10.0.1 // indirect + github.com/apache/thrift v0.16.0 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/colega/zeropool v0.0.0-20230505084239-6fb4a4f75381 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect @@ -50,16 +55,21 @@ require ( github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect + github.com/google/flatbuffers v2.0.8+incompatible // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/hashicorp/go-bexpr v0.1.14 // indirect github.com/holiman/uint256 v1.2.4 // indirect github.com/ipfs/boxo v0.10.2 // indirect + github.com/klauspost/asmfmt v1.3.2 // indirect github.com/klauspost/compress v1.17.7 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-flow-metrics v0.1.0 // indirect github.com/libp2p/go-libp2p v0.28.2 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect + github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -72,6 +82,7 @@ require ( github.com/multiformats/go-multihash v0.2.3 // indirect github.com/multiformats/go-multistream v0.4.1 // indirect github.com/multiformats/go-varint v0.0.7 // indirect + github.com/pierrec/lz4/v4 v4.1.15 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.19.0 // indirect github.com/prometheus/client_model v0.6.0 // indirect @@ -91,11 +102,15 @@ require ( github.com/x448/float16 v0.8.4 // indirect github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect + github.com/zeebo/xxh3 v1.0.2 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect + golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.24.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.20.0 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 // indirect google.golang.org/grpc v1.62.1 // indirect diff --git a/go.sum b/go.sum index 46c0febf..9ab6326c 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,23 @@ +capnproto.org/go/capnp/v3 v3.0.1-alpha.1 h1:hYEclwXEKsnu+PdHASdx3nLP0fC9kZnR+x1CEvMp9ck= +capnproto.org/go/capnp/v3 v3.0.1-alpha.1/go.mod h1:B+ZjwFmHwTYv201x6CdIo7MmDC/TROJDa00kbjTnv1s= github.com/0x19/solc-switch v1.0.4 h1:F7GiBg9kVyzp/N5l7P4aWg2Hb05Zn368vHE4e+F8Hok= github.com/0x19/solc-switch v1.0.4/go.mod h1:3Hg7nE0J84D1rbYTNwe0bXK750XJZiBXHsYIjLjxlDA= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= +github.com/apache/arrow/go/v10 v10.0.1 h1:n9dERvixoC/1JjDmBcs9FPaEryoANa2sCgVFo6ez9cI= +github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= +github.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY= +github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -40,6 +50,8 @@ github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwP github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/colega/zeropool v0.0.0-20230505084239-6fb4a4f75381 h1:d5EKgQfRQvO97jnISfR89AiCCCJMwMFoSxUiU0OGCRU= +github.com/colega/zeropool v0.0.0-20230505084239-6fb4a4f75381/go.mod h1:OU76gHeRo8xrzGJU3F3I1CqX1ekM8dfJw0+wPeMwnp0= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= @@ -92,10 +104,13 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -123,6 +138,8 @@ github.com/ipfs/go-ipfs-api v0.6.0 h1:JARgG0VTbjyVhO5ZfesnbXv9wTcMvoKRBLF1SzJqzm github.com/ipfs/go-ipfs-api v0.6.0/go.mod h1:iDC2VMwN9LUpQV/GzEeZ2zNqd8NUdRmWcFM+K/6odf0= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= @@ -150,6 +167,10 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -181,6 +202,10 @@ github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/n github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= 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/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -222,6 +247,10 @@ github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbe github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tinylib/msgp v1.1.5 h1:2gXmtWueD2HefZHQe1QOy9HVzmFrLOVvsXwXBQ0ayy0= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= +github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= @@ -240,29 +269,53 @@ github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= -go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= +github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= +gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 h1:oqta3O3AnlWbmIE3bFnWbu4bRxZjfbWCp0cKSuZh01E= google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7/go.mod h1:VQW3tUculP/D4B+xVCo+VgSq8As6wA9ZjHl//pmk+6s= google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 h1:8EeVk1VKMD+GD/neyEHGmz7pFblqPjHoi+PGQIlLx2s= diff --git a/ir/root.go b/ir/root.go index fde6302f..5ea8c9ec 100644 --- a/ir/root.go +++ b/ir/root.go @@ -255,7 +255,7 @@ func (b *Builder) processRoot(root *ast.RootNode) *RootSourceUnit { entrySourceUnit := root.GetSourceUnitById(root.GetEntrySourceUnit()) if entrySourceUnit == nil { - zap.L().Warn( + zap.L().Debug( "Entry source unit not found. Make sure it's correctly set.", zap.String("contract_address", b.GetAddress().Hex()), zap.Int64("requested_source_unit_id", root.GetEntrySourceUnit()), diff --git a/ir/standards.go b/ir/standards.go index 9e72d08a..b6fc9609 100644 --- a/ir/standards.go +++ b/ir/standards.go @@ -3,7 +3,6 @@ package ir import ( ir_pb "github.com/unpackdev/protos/dist/go/ir" "github.com/unpackdev/solgo/standards" - "github.com/unpackdev/solgo/utils" ) // Standard represents a specific Ethereum Improvement Proposal standard that a contract may adhere to. @@ -83,7 +82,7 @@ func (b *Builder) processEips(root *RootSourceUnit) { }) } else { if ret.GetTypeDescription() == nil { - utils.DumpNodeWithExit(function) + //utils.DumpNodeWithExit(function) } outputs = append(outputs, standards.Output{ Type: ret.GetTypeDescription().GetString(), diff --git a/ir/variables.go b/ir/variables.go index c5cd5b2b..d90f1ff6 100644 --- a/ir/variables.go +++ b/ir/variables.go @@ -1,7 +1,6 @@ package ir import ( - "github.com/unpackdev/solgo/utils" "strings" ast_pb "github.com/unpackdev/protos/dist/go/ast" @@ -129,7 +128,7 @@ func (b *Builder) processStateVariables(unit *ast.StateVariableDeclaration) *Sta // It could be that the name of the type name node is not set, but the type description string is. if variableNode.Type == "" { if variableNode.TypeDescription == nil { - utils.DumpNodeWithExit(variableNode) + //utils.DumpNodeWithExit(variableNode) } variableNode.Type = variableNode.TypeDescription.TypeString } diff --git a/providers/etherscan/contract.go b/providers/etherscan/contract.go index 7fa41cc6..2190751e 100644 --- a/providers/etherscan/contract.go +++ b/providers/etherscan/contract.go @@ -127,7 +127,7 @@ func (e *Provider) ScanContract(ctx context.Context, addr common.Address) (*Cont var contractResponse ContractResponse if err := json.Unmarshal(body, &contractResponse); err != nil { - return nil, fmt.Errorf("failed to unmarshal etherscan response: %s", err) + return nil, fmt.Errorf("failed to unmarshal etherscan response: %s - %s", err, url) } toReturn := contractResponse.Result[0] diff --git a/providers/etherscan/transactions.go b/providers/etherscan/transactions.go index cad51c50..80d76595 100644 --- a/providers/etherscan/transactions.go +++ b/providers/etherscan/transactions.go @@ -90,7 +90,7 @@ func (e *Provider) QueryContractCreationTx(ctx context.Context, addr common.Addr var creationResponse ContractCreationResponse if err := json.Unmarshal(body, &creationResponse); err != nil { - return nil, fmt.Errorf("failed to unmarshal contract creation response: %s", err) + return nil, fmt.Errorf("failed to unmarshal contract creation response: %s - %s", err, url) } if e.cache != nil { diff --git a/standards/standards.go b/standards/standards.go index abb9b6ff..55a6317c 100644 --- a/standards/standards.go +++ b/standards/standards.go @@ -24,6 +24,17 @@ func (s Standard) ToProto() eip_pb.Standard { return eip_pb.Standard_UNKNOWN } +// Encode manually encodes the Standard type to a byte slice. +func (s Standard) Encode() ([]byte, error) { + return []byte(s), nil +} + +// Decode manually decodes a byte slice to a Standard type. +func (s *Standard) Decode(data []byte) error { + *s = Standard(data) + return nil +} + // Constants representing various Ethereum standards and EIPs. const ( ERC20 Standard = "ERC20" // ERC-20 Token Standard. diff --git a/utils/networks.go b/utils/networks.go index 9123897d..2e23cf95 100644 --- a/utils/networks.go +++ b/utils/networks.go @@ -2,46 +2,10 @@ package utils import ( "fmt" - "github.com/enviodev/hypersync-client-go/pkg/utils" + "github.com/enviodev/hypersync-client-go/utils" "math/big" ) -type Network string -type NetworkID uint64 - -func (n NetworkID) String() string { - return n.ToBig().String() -} - -func (n NetworkID) IsValid() bool { - return n != 0 -} - -func (n NetworkID) ToNetwork() Network { - switch n { - case EthereumNetworkID: - return Ethereum - case BscNetworkID: - return Bsc - case PolygonNetworkID: - return Polygon - case AvalancheNetworkID: - return Avalanche - case FantomNetworkID: - return Fantom - case ArbitrumNetworkID: - return Arbitrum - case OptimismNetworkID: - return Optimism - default: - return Ethereum - } -} - -func (n NetworkID) Uint64() uint64 { - return uint64(n) -} - const ( AnvilNetwork Network = "anvil" Ethereum Network = "ethereum" @@ -80,10 +44,19 @@ const ( AnvilNetworkID NetworkID = 1337 ) -func (n Network) String() string { - return string(n) +type Networks []Network + +func (n Networks) GetNetworkIDs() []Network { + ids := make([]Network, 0) + for _, n := range n { + ids = append(ids, n) + } + return ids } + +type Network string + func (n Network) GetNetworkID() NetworkID { return GetNetworkID(n) } @@ -92,10 +65,51 @@ func (n Network) GetToHyperSyncNetworkID() utils.NetworkID { return utils.NetworkID(n.GetNetworkID().Uint64()) } +func (n Network) String() string { + return string(n) +} + + +type NetworkID uint64 + +func (n NetworkID) String() string { + return n.ToBig().String() +} + +func (n NetworkID) IsValid() bool { + return n != 0 +} + +func (n NetworkID) ToNetwork() Network { + switch n { + case EthereumNetworkID: + return Ethereum + case BscNetworkID: + return Bsc + case PolygonNetworkID: + return Polygon + case AvalancheNetworkID: + return Avalanche + case FantomNetworkID: + return Fantom + case ArbitrumNetworkID: + return Arbitrum + case OptimismNetworkID: + return Optimism + default: + return Ethereum + } +} + +func (n NetworkID) Uint64() uint64 { + return uint64(n) +} + func (n NetworkID) ToBig() *big.Int { return new(big.Int).SetUint64(uint64(n)) } + func GetNetworkID(network Network) NetworkID { switch network { case Ethereum: @@ -181,3 +195,15 @@ func GetNetworkFromString(network string) (Network, error) { return "", fmt.Errorf("unknown network '%s' provided", network) } } + +func GetNetworksFromStringSlice(networks []string) (Networks, error) { + toReturn := make(Networks, 0) + for _, network := range networks { + network, err := GetNetworkFromString(network) + if err != nil { + return nil, err + } + toReturn = append(toReturn, network) + } + return toReturn, nil +} \ No newline at end of file diff --git a/utils/version.go b/utils/version.go index 3c1033cc..3ec769e6 100644 --- a/utils/version.go +++ b/utils/version.go @@ -1,6 +1,8 @@ package utils import ( + "bytes" + "encoding/binary" "strconv" "strings" ) @@ -9,12 +11,33 @@ import ( // format. Optionally, a version can include a commit revision as a metadata string // appended after a plus sign (+). type SemanticVersion struct { - Major int `json:"major"` // Major version, incremented for incompatible API changes. - Minor int `json:"minor"` // Minor version, incremented for backwards-compatible enhancements. - Patch int `json:"patch"` // Patch version, incremented for backwards-compatible bug fixes. + Major int `json:"major"` // Major version, incremented for incompatible API changes. + Minor int `json:"minor"` // Minor version, incremented for backwards-compatible enhancements. + Patch int `json:"patch"` // Patch version, incremented for backwards-compatible bug fixes. Commit string `json:"revision,omitempty"` // Optional commit revision for tracking specific builds. } +// Bytes serializes the SemanticVersion into a byte slice. +func (v SemanticVersion) Bytes() []byte { + buf := new(bytes.Buffer) + + // Write Major, Minor, Patch as int32 + binary.Write(buf, binary.BigEndian, int32(v.Major)) + binary.Write(buf, binary.BigEndian, int32(v.Minor)) + binary.Write(buf, binary.BigEndian, int32(v.Patch)) + + // Write Commit with length prefix + if v.Commit != "" { + commitBytes := []byte(v.Commit) + binary.Write(buf, binary.BigEndian, uint32(len(commitBytes))) + buf.Write(commitBytes) + } else { + binary.Write(buf, binary.BigEndian, uint32(0)) // No commit + } + + return buf.Bytes() +} + // String returns the string representation of the SemanticVersion, excluding the // commit revision. It adheres to the "Major.Minor.Patch" format. func (v SemanticVersion) String() string { @@ -60,6 +83,48 @@ func ParseSemanticVersion(version string) SemanticVersion { } } +func ParseSemanticVersionFromBytes(data []byte) (SemanticVersion, error) { + buf := bytes.NewReader(data) + v := SemanticVersion{} + + var major, minor, patch int32 + var commitLen uint32 + + // Read Major, Minor, Patch + if err := binary.Read(buf, binary.BigEndian, &major); err != nil { + return v, err + } + + if err := binary.Read(buf, binary.BigEndian, &minor); err != nil { + return v, err + } + + if err := binary.Read(buf, binary.BigEndian, &patch); err != nil { + return v, err + } + + v.Major = int(major) + v.Minor = int(minor) + v.Patch = int(patch) + + // Read Commit length + if err := binary.Read(buf, binary.BigEndian, &commitLen); err != nil { + return v, err + } + + if commitLen > 0 { + commitBytes := make([]byte, commitLen) + if _, err := buf.Read(commitBytes); err != nil { + return v, err + } + v.Commit = string(commitBytes) + } else { + v.Commit = "" + } + + return v, nil +} + // IsSemanticVersionGreaterOrEqualTo checks if the version represented by a string // is greater than or equal to the provided SemanticVersion. The comparison takes // into account the Major, Minor, and Patch components of the semantic version. From a500c975fab996090a98cff69f7a651c8d105891 Mon Sep 17 00:00:00 2001 From: Nevio Date: Thu, 25 Jul 2024 19:34:59 +0200 Subject: [PATCH 05/11] Broken atm, besides that major update to abi and global structs as well as major fix to type definitions --- TODO.md | 11 +++ abi/type.go | 47 +++++++++++ ast/parameter.go | 6 ++ ast/reference.go | 6 ++ ast/type_name.go | 5 +- contracts/chain.go | 1 + contracts/constructor.go | 5 +- contracts/source.go | 22 ++++- ir/standards.go | 3 +- utils/addr.go | 170 +++++++++++++++++++++++++++++++++++++++ utils/types.go | 5 ++ 11 files changed, 276 insertions(+), 5 deletions(-) create mode 100644 TODO.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 00000000..60766660 --- /dev/null +++ b/TODO.md @@ -0,0 +1,11 @@ + +Contract Issues + +- [ ] 0x258FD2E6b5C155aa5f3e84326A622288bd70f376 +- [ ] 0x6eF81a18E1E432C289DC0d1a670B78E8bbF9AA35 +- [ ] 0x98D951E9b0C0Bb180F1b3ed40DDE6E1B1B521Cc1 +- [ ] 0xCD7ae3373F7e76A817238261b8303FA17D2AF585 +- [ ] 0xdEb43523E2857b7ec29D078c77b73709D958c62F +- [ ] 0x8CB3649114051cA5119141a34C200D65dc0Faa73 +- [ ] 0xD101dCC414F310268c37eEb4cD376CcFA507F571 +- [ ] 0x09B33A99B954e52907c61514B6f8cD37De71076f \ No newline at end of file diff --git a/abi/type.go b/abi/type.go index 6a23f989..bf8bdbd1 100644 --- a/abi/type.go +++ b/abi/type.go @@ -1,6 +1,7 @@ package abi import ( + ast_pb "github.com/unpackdev/protos/dist/go/ast" "strings" "github.com/unpackdev/solgo/ast" @@ -143,6 +144,52 @@ func (t *TypeResolver) ResolveStructType(typeName *ast.TypeDescription) MethodIO } } + // We did not discover any structs in the contracts themselves... Apparently this is a global + // struct definition... + if len(toReturn.Components) == 0 { + for _, node := range t.parser.GetAstBuilder().GetRoot().GetGlobalNodes() { + if node.GetType() == ast_pb.NodeType_STRUCT_DEFINITION { + if structVar, ok := node.(*ast.StructDefinition); ok { + if structVar.GetName() == toReturn.Name { + for _, member := range structVar.GetMembers() { + // Mapping types are not supported in structs + if isMappingType(member.GetTypeDescription().GetString()) { + continue + } + + if isContractType(member.GetTypeDescription().GetString()) { + toReturn.Outputs = append(toReturn.Outputs, MethodIO{ + Name: member.GetName(), + Type: "address", + InternalType: member.GetTypeDescription().GetString(), + }) + + continue + } + + dType := t.discoverType(member.GetTypeDescription().GetString()) + if len(dType.Outputs) > 0 { + for _, out := range dType.Outputs { + toReturn.Components = append(toReturn.Components, MethodIO{ + Name: out.Name, + Type: out.Type, + InternalType: member.GetTypeDescription().GetString(), + }) + } + } else { + toReturn.Components = append(toReturn.Components, MethodIO{ + Name: member.GetName(), + Type: dType.Type, + InternalType: member.GetTypeDescription().GetString(), + }) + } + } + } + } + } + } + } + return toReturn } diff --git a/ast/parameter.go b/ast/parameter.go index 1c94c6d6..7c2f7287 100644 --- a/ast/parameter.go +++ b/ast/parameter.go @@ -284,6 +284,12 @@ func (p *Parameter) Parse(unit *SourceUnit[Node[ast_pb.SourceUnit]], fnNode Node p.TypeName = typeName p.TypeDescription = typeName.GetTypeDescription() p.currentVariables = append(p.currentVariables, p) + + if refId, refTypeDescription := p.GetResolver().ResolveByNode(typeName, typeName.Name); refTypeDescription != nil { + typeName.ReferencedDeclaration = refId + typeName.TypeDescription = refTypeDescription + p.TypeDescription = typeName.GetTypeDescription() + } } // ParseEventParameter parses the event parameter context and populates the Parameter fields for event parameters. diff --git a/ast/reference.go b/ast/reference.go index 569b3c90..5b40d494 100644 --- a/ast/reference.go +++ b/ast/reference.go @@ -78,6 +78,12 @@ func (r *Resolver) ResolveByNode(node Node[NodeType], name string) (int64, *Type } else if isPrefixSlice && !strings.Contains(rNodeType.TypeString, "[]") { rNodeType.TypeString = "[]" + rNodeType.TypeString } + + // Somewhere in the code [] is applied to rNodeType where it should not be... + // TODO: Remove it from here and fix wherever it's happening. I don't give a crap atm about this... + if !strings.Contains(name, "[]") && strings.Contains(rNodeType.TypeString, "[]") { + rNodeType.TypeString = strings.ReplaceAll(rNodeType.TypeString, "[]", "") + } } return rNode, rNodeType diff --git a/ast/type_name.go b/ast/type_name.go index 1b6bdbd6..0b0e71be 100644 --- a/ast/type_name.go +++ b/ast/type_name.go @@ -477,7 +477,6 @@ func (t *TypeName) parseElementaryTypeName(unit *SourceUnit[Node[ast_pb.SourceUn t.TypeDescription = refTypeDescription } } - } // parseIdentifierPath parses the IdentifierPath from the given IdentifierPathContext. @@ -543,6 +542,9 @@ func (t *TypeName) parseIdentifierPath(unit *SourceUnit[Node[ast_pb.SourceUnit]] TypeString: bNormalizedTypeName, } } else { + if t.Name == "" { + t.Name = t.PathNode.Name + } if refId, refTypeDescription := t.GetResolver().ResolveByNode(t, t.Name); refTypeDescription != nil { t.ReferencedDeclaration = refId t.TypeDescription = refTypeDescription @@ -827,6 +829,7 @@ func (t *TypeName) Parse(unit *SourceUnit[Node[ast_pb.SourceUnit]], fnNode Node[ } } } + } // ParseMul parses the TypeName from the given TermalNode. diff --git a/contracts/chain.go b/contracts/chain.go index c6bb56c7..e6c460a9 100644 --- a/contracts/chain.go +++ b/contracts/chain.go @@ -26,6 +26,7 @@ func (c *Contract) DiscoverChainInfo(ctx context.Context, otsLookup bool) error } } + var txHash common.Hash /* if info == nil || info.CreationHash == utils.ZeroHash { diff --git a/contracts/constructor.go b/contracts/constructor.go index 325cdc9f..dc371f3e 100644 --- a/contracts/constructor.go +++ b/contracts/constructor.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "github.com/davecgh/go-spew/spew" "strings" "github.com/unpackdev/solgo/bytecode" @@ -29,7 +30,7 @@ func (c *Contract) DiscoverConstructor(ctx context.Context) error { abiRoot != nil && abiRoot.GetEntryContract().GetMethodByType("constructor") != nil { cAbi, _ := utils.ToJSON(abiRoot.GetEntryContract().GetMethodByType("constructor")) constructorAbi := fmt.Sprintf("[%s]", string(cAbi)) - + //utils.DumpNodeWithExit(irRoot.GetEntryContract().GetConstructor().GetParameters()) tx := c.descriptor.Transaction deployedBytecode := c.descriptor.DeployedBytecode @@ -46,7 +47,7 @@ func (c *Contract) DiscoverConstructor(ctx context.Context) error { if constructorDataIndex > len(adjustedData) { return fmt.Errorf("constructor data index out of range") } - + spew.Dump(constructorAbi) constructor, err := bytecode.DecodeConstructorFromAbi(adjustedData[constructorDataIndex:], constructorAbi) if err != nil { if !strings.Contains(err.Error(), "would go over slice boundary") { diff --git a/contracts/source.go b/contracts/source.go index fdcfa3d6..777869c6 100644 --- a/contracts/source.go +++ b/contracts/source.go @@ -30,7 +30,7 @@ func (c *Contract) DiscoverSourceCode(ctx context.Context) error { var err error // Retry mechanism - const maxRetries = 10 + const maxRetries = 15 for i := 0; i < maxRetries; i++ { dCtx, dCancel := context.WithTimeout(ctx, 15*time.Second) response, err = c.etherscan.ScanContract(dCtx, c.addr) @@ -41,6 +41,26 @@ func (c *Contract) DiscoverSourceCode(ctx context.Context) error { time.Sleep(time.Duration(i*1000) * time.Millisecond) dCancel() continue + } else if strings.Contains(err.Error(), "invalid character") { + // Wait for i*1000ms before retrying + time.Sleep(time.Duration(i*100) * time.Millisecond) + dCancel() + continue + } else if strings.Contains(err.Error(), "connection reset by peer") { + // Wait for i*1000ms before retrying + time.Sleep(time.Duration(i*100) * time.Millisecond) + dCancel() + continue + } else if strings.Contains(err.Error(), "i/o timeout") { + // Wait for i*1000ms before retrying + time.Sleep(time.Duration(i*100) * time.Millisecond) + dCancel() + continue + } else if strings.Contains(err.Error(), "EOF") { + // Wait for i*1000ms before retrying + time.Sleep(time.Duration(i*100) * time.Millisecond) + dCancel() + continue } else if !strings.Contains(err.Error(), "not found") && !strings.Contains(err.Error(), "not verified") { zap.L().Error( diff --git a/ir/standards.go b/ir/standards.go index b6fc9609..9e72d08a 100644 --- a/ir/standards.go +++ b/ir/standards.go @@ -3,6 +3,7 @@ package ir import ( ir_pb "github.com/unpackdev/protos/dist/go/ir" "github.com/unpackdev/solgo/standards" + "github.com/unpackdev/solgo/utils" ) // Standard represents a specific Ethereum Improvement Proposal standard that a contract may adhere to. @@ -82,7 +83,7 @@ func (b *Builder) processEips(root *RootSourceUnit) { }) } else { if ret.GetTypeDescription() == nil { - //utils.DumpNodeWithExit(function) + utils.DumpNodeWithExit(function) } outputs = append(outputs, standards.Output{ Type: ret.GetTypeDescription().GetString(), diff --git a/utils/addr.go b/utils/addr.go index feb3c2a9..a0f002e9 100644 --- a/utils/addr.go +++ b/utils/addr.go @@ -2,6 +2,8 @@ package utils import ( "github.com/ethereum/go-ethereum/common" + "bytes" + "encoding/binary" ) var ( @@ -38,3 +40,171 @@ func (n NamedAddr) AppendTags(tags ...string) NamedAddr { func (n NamedAddr) IsZeroAddress() bool { return n.Addr == ZeroAddress } + +// NamedAddr encapsulates detailed information about an Ethereum address along with its metadata. +// It includes a human-readable name, an optional Ethereum Name Service (ENS) domain, a set of +// tags for categorization or annotation, the Ethereum address itself, and the type of address +// which provides additional context about its use or origin. +type Addr struct { + NetworkId NetworkID `json:"networkId"` + Name string `json:"name"` + Ens string `json:"ens"` + Tags []string `json:"tags"` + Addr common.Address `json:"addr"` + Type AddressType `json:"type"` +} + +func (n *Addr) Hex() string { + return n.Addr.Hex() +} + +// AppendTags appends unique tags to the named address +func (n *Addr) AppendTags(tags ...string) *Addr { + for _, tag := range tags { + if !StringInSlice(tag, n.Tags) { + n.Tags = append(n.Tags, tag) + } + } + return n +} + +// IsZeroAddress checks whenever named address is zero address +func (n *Addr) IsZeroAddress() bool { + return n.Addr == ZeroAddress +} + +// Encode serializes the Addr into bytes +func (n *Addr) Encode() ([]byte, error) { + var buf bytes.Buffer + + // Encode NetworkId + if err := binary.Write(&buf, binary.LittleEndian, n.NetworkId.Uint64()); err != nil { + return nil, err + } + + // Encode Name + nameBytes := []byte(n.Name) + nameLen := uint32(len(nameBytes)) + if err := binary.Write(&buf, binary.LittleEndian, nameLen); err != nil { + return nil, err + } + if _, err := buf.Write(nameBytes); err != nil { + return nil, err + } + + // Encode ENS + ensBytes := []byte(n.Ens) + ensLen := uint32(len(ensBytes)) + if err := binary.Write(&buf, binary.LittleEndian, ensLen); err != nil { + return nil, err + } + if _, err := buf.Write(ensBytes); err != nil { + return nil, err + } + + // Encode Tags + tagsLen := uint32(len(n.Tags)) + if err := binary.Write(&buf, binary.LittleEndian, tagsLen); err != nil { + return nil, err + } + + for _, tag := range n.Tags { + tagBytes := []byte(tag) + tagLen := uint32(len(tagBytes)) + if err := binary.Write(&buf, binary.LittleEndian, tagLen); err != nil { + return nil, err + } + if _, err := buf.Write(tagBytes); err != nil { + return nil, err + } + } + + // Encode Addr + if _, err := buf.Write(n.Addr.Bytes()); err != nil { + return nil, err + } + + // Encode Type + typeBytes := []byte(n.Type) + typeLen := uint32(len(typeBytes)) + if err := binary.Write(&buf, binary.LittleEndian, typeLen); err != nil { + return nil, err + } + if _, err := buf.Write(typeBytes); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +// Decode deserializes bytes into Addr +func (n *Addr) Decode(data []byte) error { + buf := bytes.NewReader(data) + + // Decode NetworkId + var networkId uint64 + if err := binary.Read(buf, binary.LittleEndian, &networkId); err != nil { + return err + } + n.NetworkId = NetworkID(networkId) + + // Decode Name + var nameLen uint32 + if err := binary.Read(buf, binary.LittleEndian, &nameLen); err != nil { + return err + } + nameBytes := make([]byte, nameLen) + if _, err := buf.Read(nameBytes); err != nil { + return err + } + n.Name = string(nameBytes) + + // Decode ENS + var ensLen uint32 + if err := binary.Read(buf, binary.LittleEndian, &ensLen); err != nil { + return err + } + ensBytes := make([]byte, ensLen) + if _, err := buf.Read(ensBytes); err != nil { + return err + } + n.Ens = string(ensBytes) + + // Decode Tags + var tagsLen uint32 + if err := binary.Read(buf, binary.LittleEndian, &tagsLen); err != nil { + return err + } + n.Tags = make([]string, tagsLen) + for i := uint32(0); i < tagsLen; i++ { + var tagLen uint32 + if err := binary.Read(buf, binary.LittleEndian, &tagLen); err != nil { + return err + } + tagBytes := make([]byte, tagLen) + if _, err := buf.Read(tagBytes); err != nil { + return err + } + n.Tags[i] = string(tagBytes) + } + + // Decode Addr + addrBytes := make([]byte, common.AddressLength) + if _, err := buf.Read(addrBytes); err != nil { + return err + } + n.Addr = common.BytesToAddress(addrBytes) + + // Decode Type + var typeLen uint32 + if err := binary.Read(buf, binary.LittleEndian, &typeLen); err != nil { + return err + } + typeBytes := make([]byte, typeLen) + if _, err := buf.Read(typeBytes); err != nil { + return err + } + n.Type = AddressType(typeBytes) + + return nil +} diff --git a/utils/types.go b/utils/types.go index 82da1c88..c867d24c 100644 --- a/utils/types.go +++ b/utils/types.go @@ -121,6 +121,11 @@ const ( AddressRecipient AddressType = "address" ContractRecipient AddressType = "contract" + ZeroAddressType AddressType = "zero_address" + AccountAddressType AddressType = "account" + ContractAddressType AddressType = "contract" + + UnknownTransactionMethodType TransactionMethodType = "unknown" ContractCreationType TransactionMethodType = "contract_creation" ApproveMethodType TransactionMethodType = "approve" From cb296224da0473ca2c034e877c70289fcb3b6417 Mon Sep 17 00:00:00 2001 From: Nevio Date: Thu, 25 Jul 2024 21:06:50 +0200 Subject: [PATCH 06/11] Major update to sources - we now save soruces as they are instead of foobared, stripped. Resolved issues with import reference search --- abi/state_variable.go | 4 ++++ ast/imports.go | 2 +- ast/reference.go | 9 ++++----- ast/type_name.go | 11 +++++++++-- sources.go | 6 ++++-- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/abi/state_variable.go b/abi/state_variable.go index c7818639..85bfde5b 100644 --- a/abi/state_variable.go +++ b/abi/state_variable.go @@ -16,6 +16,10 @@ func (b *Builder) processStateVariable(stateVar *ir.StateVariable) *Method { StateMutability: b.normalizeStateMutability(stateVar.GetStateMutability()), } + /* if stateVar.GetTypeDescription() == nil { + utils.DumpNodeWithExit(stateVar) + }*/ + typeName := b.resolver.ResolveType(stateVar.GetTypeDescription()) switch typeName { diff --git a/ast/imports.go b/ast/imports.go index 657eca20..44d71bdc 100644 --- a/ast/imports.go +++ b/ast/imports.go @@ -212,7 +212,7 @@ func parseImportPathsForSourceUnit( } if importCtx.As() != nil { - importNode.UnitAlias = importCtx.As().GetText() + importNode.As = importCtx.As().GetText() } if importCtx.SymbolAliases() != nil { diff --git a/ast/reference.go b/ast/reference.go index 5b40d494..82ba43ae 100644 --- a/ast/reference.go +++ b/ast/reference.go @@ -339,17 +339,12 @@ func (r *Resolver) resolveEntrySourceUnit() { // resolveImportDirectives resolves import directives in the AST. func (r *Resolver) byImport(name string, baseNode Node[NodeType]) (int64, *TypeDescription) { - // In case any imports are available and they are not exported // we are going to append them to the exported symbols. for _, node := range r.ASTBuilder.currentImports { if node.GetType() == ast_pb.NodeType_IMPORT_DIRECTIVE { importNode := node.(*Import) - if baseNode.GetType() != ast_pb.NodeType_IMPORT_DIRECTIVE { - continue - } - if importNode.GetName() == name { return importNode.GetId(), importNode.GetTypeDescription() } @@ -563,6 +558,10 @@ func (r *Resolver) byStructs(name string) (int64, *TypeDescription) { name = strings.Split(name, ".")[1] } + if strings.Contains(name, "[]") { + name = strings.ReplaceAll(name, "[]", "") + } + for _, node := range r.currentStructs { structNode := node.(*StructDefinition) if structNode.GetName() == name { diff --git a/ast/type_name.go b/ast/type_name.go index 0b0e71be..6a57afcc 100644 --- a/ast/type_name.go +++ b/ast/type_name.go @@ -486,8 +486,15 @@ func (t *TypeName) parseIdentifierPath(unit *SourceUnit[Node[ast_pb.SourceUnit]] if len(ctx.AllIdentifier()) > 0 { identifierCtx := ctx.Identifier(0) t.PathNode = &PathNode{ - Id: t.GetNextID(), - Name: identifierCtx.GetText(), + Id: t.GetNextID(), + Name: func() string { + if len(ctx.AllIdentifier()) == 1 { + return identifierCtx.GetText() + } else if len(ctx.AllIdentifier()) == 2 { + return fmt.Sprintf("%s.%s", identifierCtx.GetText(), ctx.Identifier(1).GetText()) + } + return "" + }(), Src: SrcNode{ Line: int64(ctx.GetStart().GetLine()), Column: int64(ctx.GetStart().GetColumn()), diff --git a/sources.go b/sources.go index 596a386a..8f778ec0 100644 --- a/sources.go +++ b/sources.go @@ -750,11 +750,13 @@ func (s *Sources) WriteToDir(path string) error { // Write each SourceUnit's content to a file in the specified directory for _, sourceUnit := range s.SourceUnits { - content := utils.SimplifyImportPaths(sourceUnit.Content) + // WARN: Left it here as a future - BIG NO NO - import paths will be skewed and won't able to parse + // properly + we won't be able to share sources with everyone else as-they-are... + // content := utils.SimplifyImportPaths(sourceUnit.Content) filePath := filepath.Join(path, sourceUnit.Name+".sol") - if err := utils.WriteToFile(filePath, []byte(content)); err != nil { + if err := utils.WriteToFile(filePath, []byte(sourceUnit.Content)); err != nil { return fmt.Errorf("failed to write source unit %s to file: %v", sourceUnit.Name, err) } } From 3c6f2c7813001691c520ea77d28ce34d23306231 Mon Sep 17 00:00:00 2001 From: Nevio Date: Fri, 26 Jul 2024 12:07:50 +0200 Subject: [PATCH 07/11] Attempt to at least resolve stupid panics... --- parser/solidity_parser.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/parser/solidity_parser.go b/parser/solidity_parser.go index c33b3370..e66e2be9 100644 --- a/parser/solidity_parser.go +++ b/parser/solidity_parser.go @@ -25049,7 +25049,11 @@ func (p *SolidityParser) Sempred(localctx antlr.RuleContext, ruleIndex, predInde case 30: var t *StateVariableDeclarationContext = nil if localctx != nil { - t = localctx.(*StateVariableDeclarationContext) + if tCtx, ok := localctx.(*StateVariableDeclarationContext); ok { + t = tCtx + } else if _, ok := localctx.(*IdentifierPathContext); ok { + return true + } } return p.StateVariableDeclaration_Sempred(t, predIndex) From 2185b26c6073c79abdd331bde1e5cb2a5ca5dc87 Mon Sep 17 00:00:00 2001 From: Nevio Date: Fri, 26 Jul 2024 14:30:44 +0200 Subject: [PATCH 08/11] Major improvements to the PathNode and Solidity v0.4.... parsing --- TODO.md | 9 ++- abi/error.go | 6 +- abi/state_variable.go | 5 +- abi/type.go | 7 +- ast/error.go | 1 - ast/reference.go | 24 +++---- ast/source_unit.go | 3 + ast/storage.go | 13 ++++ ast/struct.go | 23 +++---- ast/tree.go | 4 +- ast/type_name.go | 143 +++++++++++++++++++++++++++++++++------ ast/user_defined.go | 1 + contracts/constructor.go | 10 ++- ir/root.go | 2 +- ir/variables.go | 3 +- storage/reader.go | 3 +- 16 files changed, 198 insertions(+), 59 deletions(-) diff --git a/TODO.md b/TODO.md index 60766660..afeeb0b1 100644 --- a/TODO.md +++ b/TODO.md @@ -1,11 +1,16 @@ Contract Issues -- [ ] 0x258FD2E6b5C155aa5f3e84326A622288bd70f376 +- [x] 0x258FD2E6b5C155aa5f3e84326A622288bd70f376 +- [ ] 0x2aa101BF99CaeF7fc1355D4c493a1fe187A007cE +- [ ] 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84 +- [ ] 0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4 +- [ ] 0x609c690e8F7D68a59885c9132e812eEbDaAf0c9e +- [ ] 0x76264869a3eBF51a59FCa5ABa84ee2867c7F190e - [ ] 0x6eF81a18E1E432C289DC0d1a670B78E8bbF9AA35 - [ ] 0x98D951E9b0C0Bb180F1b3ed40DDE6E1B1B521Cc1 - [ ] 0xCD7ae3373F7e76A817238261b8303FA17D2AF585 - [ ] 0xdEb43523E2857b7ec29D078c77b73709D958c62F - [ ] 0x8CB3649114051cA5119141a34C200D65dc0Faa73 - [ ] 0xD101dCC414F310268c37eEb4cD376CcFA507F571 -- [ ] 0x09B33A99B954e52907c61514B6f8cD37De71076f \ No newline at end of file +- [ ] 0x09B33A99B954e52907c61514B6f8cD37De71076f diff --git a/abi/error.go b/abi/error.go index c35e8025..73341651 100644 --- a/abi/error.go +++ b/abi/error.go @@ -1,8 +1,6 @@ package abi import ( - "fmt" - "github.com/unpackdev/solgo/ir" ) @@ -19,7 +17,9 @@ func (b *Builder) processError(unit *ir.Error) (*Method, error) { for _, parameter := range unit.GetParameters() { if parameter.GetTypeDescription() == nil { - return nil, fmt.Errorf("nil type description for error parameter %s", parameter.GetName()) + //utils.DumpNodeWithExit(unit) + //return nil, fmt.Errorf("nil type description for error parameter %s", parameter.GetName()) + continue } methodIo := MethodIO{ diff --git a/abi/state_variable.go b/abi/state_variable.go index 85bfde5b..b804290d 100644 --- a/abi/state_variable.go +++ b/abi/state_variable.go @@ -2,6 +2,7 @@ package abi import ( "github.com/unpackdev/solgo/ir" + "github.com/unpackdev/solgo/utils" ) // processStateVariable processes the provided StateVariable from the IR and constructs a Method representation. @@ -16,9 +17,9 @@ func (b *Builder) processStateVariable(stateVar *ir.StateVariable) *Method { StateMutability: b.normalizeStateMutability(stateVar.GetStateMutability()), } - /* if stateVar.GetTypeDescription() == nil { + if stateVar.GetTypeDescription() == nil { utils.DumpNodeWithExit(stateVar) - }*/ + } typeName := b.resolver.ResolveType(stateVar.GetTypeDescription()) diff --git a/abi/type.go b/abi/type.go index bf8bdbd1..e7c0fe87 100644 --- a/abi/type.go +++ b/abi/type.go @@ -97,10 +97,15 @@ func (t *TypeResolver) ResolveStructType(typeName *ast.TypeDescription) MethodIO nameCleaned = strings.TrimRight(nameCleaned, "[]") nameParts := strings.Split(nameCleaned, ".") + methodType := "tuple" + if strings.Contains(typeName.GetString(), "[]") { + methodType = "tuple[]" + } + toReturn := MethodIO{ Name: nameParts[1], Components: make([]MethodIO, 0), - Type: "tuple", + Type: methodType, InternalType: typeName.GetString(), } diff --git a/ast/error.go b/ast/error.go index 2fa9b193..e0b4dd1a 100644 --- a/ast/error.go +++ b/ast/error.go @@ -2,7 +2,6 @@ package ast import ( "fmt" - ast_pb "github.com/unpackdev/protos/dist/go/ast" "github.com/unpackdev/solgo/parser" ) diff --git a/ast/reference.go b/ast/reference.go index 82ba43ae..347186f6 100644 --- a/ast/reference.go +++ b/ast/reference.go @@ -66,7 +66,8 @@ func (r *Resolver) ResolveByNode(node Node[NodeType], name string) (int64, *Type // Node could not be found in this moment, we are going to see if we can discover it in the // future at the end of whole parsing process. - if rNodeType == nil { + // It can be that within the import it's discovered and we have to move away from it. + if rNodeType == nil || rNodeType.TypeString == "import" { r.UnprocessedNodes[node.GetId()] = UnprocessedNode{ Id: node.GetId(), Name: name, @@ -79,11 +80,13 @@ func (r *Resolver) ResolveByNode(node Node[NodeType], name string) (int64, *Type rNodeType.TypeString = "[]" + rNodeType.TypeString } + //fmt.Println(name, cleanedName, isSlice, isPrefixSlice, rNodeType.TypeString, rNode) + // Somewhere in the code [] is applied to rNodeType where it should not be... // TODO: Remove it from here and fix wherever it's happening. I don't give a crap atm about this... - if !strings.Contains(name, "[]") && strings.Contains(rNodeType.TypeString, "[]") { - rNodeType.TypeString = strings.ReplaceAll(rNodeType.TypeString, "[]", "") - } + /* if !strings.Contains(name, "[]") && strings.Contains(rNodeType.TypeString, "[]") { + rNodeType.TypeString = strings.ReplaceAll(rNodeType.TypeString, "[]", "") + }*/ } return rNode, rNodeType @@ -96,10 +99,6 @@ func (r *Resolver) resolveByNode(name string, baseNode Node[NodeType]) (int64, * return node, nodeType } - if node, nodeType := r.byGlobals(name); nodeType != nil { - return node, nodeType - } - if node, nodeType := r.byStateVariables(name); nodeType != nil { return node, nodeType } @@ -136,6 +135,10 @@ func (r *Resolver) resolveByNode(name string, baseNode Node[NodeType]) (int64, * return node, nodeType } + if node, nodeType := r.byGlobals(name); nodeType != nil { + return node, nodeType + } + if node, nodeType := r.byImport(name, baseNode); nodeType != nil { return node, nodeType } @@ -345,10 +348,6 @@ func (r *Resolver) byImport(name string, baseNode Node[NodeType]) (int64, *TypeD if node.GetType() == ast_pb.NodeType_IMPORT_DIRECTIVE { importNode := node.(*Import) - if importNode.GetName() == name { - return importNode.GetId(), importNode.GetTypeDescription() - } - if importNode.GetUnitAlias() == name { return importNode.GetId(), importNode.GetTypeDescription() } @@ -564,6 +563,7 @@ func (r *Resolver) byStructs(name string) (int64, *TypeDescription) { for _, node := range r.currentStructs { structNode := node.(*StructDefinition) + if structNode.GetName() == name { return node.GetId(), node.GetTypeDescription() } diff --git a/ast/source_unit.go b/ast/source_unit.go index 1af0b800..16503e02 100644 --- a/ast/source_unit.go +++ b/ast/source_unit.go @@ -311,6 +311,7 @@ func (b *ASTBuilder) EnterSourceUnit(ctx *parser.SourceUnitContext) { sourceUnit.Kind = ast_pb.NodeType_KIND_INTERFACE interfaceNode := NewInterfaceDefinition(b) interfaceNode.Parse(ctx, interfaceCtx, rootNode, sourceUnit) + //fmt.Println("Interface found...", interfaceCtx.Identifier().GetText()) b.sourceUnits = append(b.sourceUnits, sourceUnit) } @@ -320,6 +321,7 @@ func (b *ASTBuilder) EnterSourceUnit(ctx *parser.SourceUnitContext) { sourceUnit.Kind = ast_pb.NodeType_KIND_LIBRARY libraryNode := NewLibraryDefinition(b) libraryNode.Parse(ctx, libraryCtx, rootNode, sourceUnit) + //fmt.Println("Library found...", libraryCtx.Identifier().GetText()) b.sourceUnits = append(b.sourceUnits, sourceUnit) } @@ -329,6 +331,7 @@ func (b *ASTBuilder) EnterSourceUnit(ctx *parser.SourceUnitContext) { sourceUnit.Kind = ast_pb.NodeType_KIND_CONTRACT contractNode := NewContractDefinition(b) contractNode.Parse(ctx, contractCtx, rootNode, sourceUnit) + //fmt.Println("Contract found...", contractCtx.Identifier().GetText()) b.sourceUnits = append(b.sourceUnits, sourceUnit) } } diff --git a/ast/storage.go b/ast/storage.go index 1de38773..63b84907 100644 --- a/ast/storage.go +++ b/ast/storage.go @@ -3,6 +3,7 @@ package ast import ( "fmt" ast_pb "github.com/unpackdev/protos/dist/go/ast" + "math" "strconv" "strings" ) @@ -52,6 +53,18 @@ func (t *TypeName) StorageSize() (int64, bool) { return 256, true } + if strings.Contains(t.GetTypeDescription().GetString(), "int_const") { + rationalParts := strings.Split(t.GetTypeDescription().GetIdentifier(), "_by_") + if len(rationalParts) == 2 { + numerator, err1 := strconv.Atoi(rationalParts[0][len(rationalParts[0])-2:]) + denominator, err2 := strconv.Atoi(rationalParts[1]) + if err1 == nil && err2 == nil { + bitSize := int64(math.Ceil(math.Log2(float64(numerator / denominator)))) + return bitSize, true + } + } + } + return 0, false // Add cases for other node types like struct, enum, etc., as needed. diff --git a/ast/struct.go b/ast/struct.go index 6ab2c03a..7ec2ac95 100644 --- a/ast/struct.go +++ b/ast/struct.go @@ -3,7 +3,6 @@ package ast import ( "fmt" "github.com/goccy/go-json" - ast_pb "github.com/unpackdev/protos/dist/go/ast" "github.com/unpackdev/solgo/parser" ) @@ -11,18 +10,18 @@ import ( // StructDefinition represents a struct definition in the Solidity abstract syntax tree (AST). type StructDefinition struct { *ASTBuilder // Embedding the ASTBuilder for common functionality - SourceUnitName string `json:"-"` // Name of the source unit - Id int64 `json:"id"` // Unique identifier for the struct definition - NodeType ast_pb.NodeType `json:"nodeType"` // Type of the node (STRUCT_DEFINITION for struct definition) - Src SrcNode `json:"src"` // Source information about the struct definition - Name string `json:"name"` // Name of the struct - NameLocation SrcNode `json:"nameLocation"` // Source information about the name of the struct - CanonicalName string `json:"canonicalName"` // Canonical name of the struct + SourceUnitName string `json:"-"` // Name of the source unit + Id int64 `json:"id"` // Unique identifier for the struct definition + NodeType ast_pb.NodeType `json:"nodeType"` // Type of the node (STRUCT_DEFINITION for struct definition) + Src SrcNode `json:"src"` // Source information about the struct definition + Name string `json:"name"` // Name of the struct + NameLocation SrcNode `json:"nameLocation"` // Source information about the name of the struct + CanonicalName string `json:"canonicalName"` // Canonical name of the struct ReferencedDeclaration int64 `json:"referencedDeclaration,omitempty"` // Referenced declaration of the struct definition - TypeDescription *TypeDescription `json:"typeDescription"` // Type description of the struct definition - Members []Node[NodeType] `json:"members"` // Members of the struct definition - Visibility ast_pb.Visibility `json:"visibility"` // Visibility of the struct definition - StorageLocation ast_pb.StorageLocation `json:"storageLocation"` // Storage location of the struct definition + TypeDescription *TypeDescription `json:"typeDescription"` // Type description of the struct definition + Members []Node[NodeType] `json:"members"` // Members of the struct definition + Visibility ast_pb.Visibility `json:"visibility"` // Visibility of the struct definition + StorageLocation ast_pb.StorageLocation `json:"storageLocation"` // Storage location of the struct definition } // NewStructDefinition creates a new StructDefinition instance. diff --git a/ast/tree.go b/ast/tree.go index 07c0a541..4a1b6ac5 100644 --- a/ast/tree.go +++ b/ast/tree.go @@ -70,13 +70,13 @@ func (t *Tree) UpdateNodeReferenceById(nodeId int64, nodeRefId int64, typeRef *T return false } - for _, child := range t.astRoot.GetGlobalNodes() { + for _, child := range t.astRoot.GetNodes() { if n := t.byRecursiveReferenceUpdate(child, nodeId, nodeRefId, typeRef); n { return n } } - for _, child := range t.astRoot.GetNodes() { + for _, child := range t.astRoot.GetGlobalNodes() { if n := t.byRecursiveReferenceUpdate(child, nodeId, nodeRefId, typeRef); n { return n } diff --git a/ast/type_name.go b/ast/type_name.go index 6a57afcc..1c5193f6 100644 --- a/ast/type_name.go +++ b/ast/type_name.go @@ -62,9 +62,32 @@ func (t *TypeName) WithParentNode(p Node[NodeType]) { // SetReferenceDescriptor sets the reference descriptions of the TypeName node. func (t *TypeName) SetReferenceDescriptor(refId int64, refDesc *TypeDescription) bool { + if t.TypeDescription != nil { + return true + } + t.ReferencedDeclaration = refId t.TypeDescription = refDesc + // Well this is a mother-fiasco... + // This whole TypeName needs to be revisited in the future entirely... + // Prototype that's now used on production... + if strings.HasSuffix(t.Name, "[]") && refDesc != nil { + if !strings.HasSuffix(refDesc.TypeString, "[]") { + t.TypeDescription.TypeString += "[]" + /*if t.PathNode != nil && t.PathNode.TypeDescription != nil { + if !strings.HasSuffix(t.PathNode.Name, "[]") { + if strings.HasSuffix(t.PathNode.TypeDescription.TypeString, "[]") { + // Kumbayaa through fucking recursion... + t.PathNode.TypeDescription.TypeString = strings.TrimSuffix( + t.PathNode.TypeDescription.TypeString, "[]", + ) + } + } + }*/ + } + } + // Lets update the parent node as well in case that type description is not set... /* parentNodeId := t.GetSrc().GetParentIndex() @@ -489,9 +512,15 @@ func (t *TypeName) parseIdentifierPath(unit *SourceUnit[Node[ast_pb.SourceUnit]] Id: t.GetNextID(), Name: func() string { if len(ctx.AllIdentifier()) == 1 { + if t.Name == "" { + t.Name = identifierCtx.GetText() + } return identifierCtx.GetText() } else if len(ctx.AllIdentifier()) == 2 { - return fmt.Sprintf("%s.%s", identifierCtx.GetText(), ctx.Identifier(1).GetText()) + if t.Name == "" { + t.Name = identifierCtx.GetText() + } + return ctx.Identifier(1).GetText() } return "" }(), @@ -531,33 +560,110 @@ func (t *TypeName) parseIdentifierPath(unit *SourceUnit[Node[ast_pb.SourceUnit]] TypeString: normalizedTypeName, } } else { - if refId, refTypeDescription := t.GetResolver().ResolveByNode(t.PathNode, identifierCtx.GetText()); refTypeDescription != nil { - t.PathNode.ReferencedDeclaration = refId - t.PathNode.TypeDescription = refTypeDescription + if len(ctx.AllIdentifier()) == 2 { + if refId, refTypeDescription := t.GetResolver().ResolveByNode(t.PathNode, ctx.Identifier(1).GetText()); refTypeDescription != nil { + t.PathNode.ReferencedDeclaration = refId + t.PathNode.TypeDescription = refTypeDescription + } + } else { + if refId, refTypeDescription := t.GetResolver().ResolveByNode(t.PathNode, identifierCtx.GetText()); refTypeDescription != nil { + t.PathNode.ReferencedDeclaration = refId + t.PathNode.TypeDescription = refTypeDescription + } } } - bNormalizedTypeName, bNormalizedTypeIdentifier, bFound := normalizeTypeDescriptionWithStatus( - identifierCtx.GetText(), - ) + // There can be messages that are basically special... + if strings.Contains(ctx.GetText(), "msg.") { + t.TypeDescription = &TypeDescription{ + TypeIdentifier: "t_magic_message", + TypeString: "msg", + } + } - // Alright lets now figure out main type description as it can be different such as - // PathNode vs PathNode[] - if bFound { + if strings.Contains(ctx.GetText(), "block.") { t.TypeDescription = &TypeDescription{ - TypeIdentifier: bNormalizedTypeIdentifier, - TypeString: bNormalizedTypeName, + TypeIdentifier: "t_magic_block", + TypeString: "block", } - } else { - if t.Name == "" { - t.Name = t.PathNode.Name + } + + if strings.Contains(ctx.GetText(), "abi") { + t.TypeDescription = &TypeDescription{ + TypeIdentifier: "t_magic_abi", + TypeString: "abi", } - if refId, refTypeDescription := t.GetResolver().ResolveByNode(t, t.Name); refTypeDescription != nil { - t.ReferencedDeclaration = refId - t.TypeDescription = refTypeDescription + } + + // For now just like this but in the future we should look into figuring out which contract + // is being referenced here... + // We would need to search for function declarations and match them accordingly... + if strings.Contains(ctx.GetText(), "super") { + t.TypeDescription = &TypeDescription{ + TypeIdentifier: "t_magic_super", + TypeString: "super", } } + + // This is a magic this type and should be treated by setting type description to the contract type + if strings.Contains(ctx.GetText(), "this") { + if unit == nil { + t.TypeDescription = &TypeDescription{ + TypeIdentifier: "t_magic_this", + TypeString: "this", + } + } else { + t.TypeDescription = unit.GetTypeDescription() + } + } + + if ctx.GetText() == "now" { + t.TypeDescription = &TypeDescription{ + TypeIdentifier: "t_magic_now", + TypeString: "now", + } + } + + if strings.Contains(ctx.GetText(), "tx.") { + t.TypeDescription = &TypeDescription{ + TypeIdentifier: "t_magic_transaction", + TypeString: "tx", + } + } + + if ctx.GetText() == "origin" { + t.TypeDescription = &TypeDescription{ + TypeIdentifier: "t_address", + TypeString: "address", + } + } + + if t.TypeDescription == nil { + bNormalizedTypeName, bNormalizedTypeIdentifier, bFound := normalizeTypeDescriptionWithStatus( + identifierCtx.GetText(), + ) + + // Alright lets now figure out main type description as it can be different such as + // PathNode vs PathNode[] + if bFound { + t.TypeDescription = &TypeDescription{ + TypeIdentifier: bNormalizedTypeIdentifier, + TypeString: bNormalizedTypeName, + } + } else { + if refId, refTypeDescription := t.GetResolver().ResolveByNode(t, t.Name); refTypeDescription != nil { + t.ReferencedDeclaration = refId + t.TypeDescription = refTypeDescription + } + } + } + + /* if t.Id == 2404 { + fmt.Println(ctx.GetText()) + utils.DumpNodeWithExit(t) + }*/ } + } // parseMappingTypeName parses the MappingTypeName from the given MappingTypeContext. @@ -738,7 +844,6 @@ func (t *TypeName) generateTypeName(sourceUnit *SourceUnit[Node[ast_pb.SourceUni t.parseIdentifierPath(sourceUnit, parentNode.GetId(), specificCtx.IdentifierPath().(*parser.IdentifierPathContext)) } else { - normalizedTypeName, normalizedTypeIdentifier := normalizeTypeDescription( typeName.Name, ) diff --git a/ast/user_defined.go b/ast/user_defined.go index ef87cea7..07451808 100644 --- a/ast/user_defined.go +++ b/ast/user_defined.go @@ -35,6 +35,7 @@ func NewUserDefinedValueTypeDefinition(b *ASTBuilder) *UserDefinedValueTypeDefin func (b *UserDefinedValueTypeDefinition) SetReferenceDescriptor(refId int64, refDesc *TypeDescription) bool { b.ReferencedDeclaration = refId b.TypeDescription = refDesc + panic("Here...") if b.TypeName != nil { b.TypeName.SetReferenceDescriptor(refId, refDesc) diff --git a/contracts/constructor.go b/contracts/constructor.go index dc371f3e..99991d66 100644 --- a/contracts/constructor.go +++ b/contracts/constructor.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "fmt" - "github.com/davecgh/go-spew/spew" "strings" "github.com/unpackdev/solgo/bytecode" @@ -36,6 +35,7 @@ func (c *Contract) DiscoverConstructor(ctx context.Context) error { // Ensure that empty bytecode is not processed, otherwise: // panic: runtime error: slice bounds out of range [:20] with capacity 0 + // TODO: Consider applying error here, not sure if we should really. if len(deployedBytecode) < 20 { return nil } @@ -47,7 +47,13 @@ func (c *Contract) DiscoverConstructor(ctx context.Context) error { if constructorDataIndex > len(adjustedData) { return fmt.Errorf("constructor data index out of range") } - spew.Dump(constructorAbi) + + // TODO: Fix + // - 0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936 (239 bytes more) - Some shitty text... + + //spew.Dump(hex.EncodeToString(adjustedData[constructorDataIndex:])) + //spew.Dump(constructorAbi) // 239 + //utils.DumpNodeWithExit(irRoot.GetEntryContract().GetConstructor().GetParameters()) constructor, err := bytecode.DecodeConstructorFromAbi(adjustedData[constructorDataIndex:], constructorAbi) if err != nil { if !strings.Contains(err.Error(), "would go over slice boundary") { diff --git a/ir/root.go b/ir/root.go index 5ea8c9ec..bff9fb64 100644 --- a/ir/root.go +++ b/ir/root.go @@ -242,7 +242,7 @@ func (b *Builder) processRoot(root *ast.RootNode) *RootSourceUnit { builder: b, Unit: root, NodeType: root.GetType(), - ContractsCount: int32(root.GetSourceUnitCount()), + ContractsCount: root.GetSourceUnitCount(), Contracts: make([]*Contract, 0), ContractTypes: make([]string, 0), Standards: make([]*Standard, 0), diff --git a/ir/variables.go b/ir/variables.go index d90f1ff6..c5cd5b2b 100644 --- a/ir/variables.go +++ b/ir/variables.go @@ -1,6 +1,7 @@ package ir import ( + "github.com/unpackdev/solgo/utils" "strings" ast_pb "github.com/unpackdev/protos/dist/go/ast" @@ -128,7 +129,7 @@ func (b *Builder) processStateVariables(unit *ast.StateVariableDeclaration) *Sta // It could be that the name of the type name node is not set, but the type description string is. if variableNode.Type == "" { if variableNode.TypeDescription == nil { - //utils.DumpNodeWithExit(variableNode) + utils.DumpNodeWithExit(variableNode) } variableNode.Type = variableNode.TypeDescription.TypeString } diff --git a/storage/reader.go b/storage/reader.go index d03c5f2f..3dd02f56 100644 --- a/storage/reader.go +++ b/storage/reader.go @@ -3,6 +3,7 @@ package storage import ( "context" "fmt" + "github.com/unpackdev/solgo/utils" "strings" ) @@ -74,7 +75,7 @@ func (r *Reader) CalculateStorageLayout() error { for _, variable := range variables { storageSize, found := variable.GetAST().GetTypeName().StorageSize() if !found { - //utils.DumpNodeWithExit(variable.GetAST().GetTypeName()) + utils.DumpNodeWithExit(variable.GetAST().GetTypeName()) return fmt.Errorf("error calculating storage size for variable: %s", variable.GetName()) } From 9067b4c503847de2bd9beec2f58efd81bb40b18b Mon Sep 17 00:00:00 2001 From: Nevio Date: Mon, 29 Jul 2024 16:09:36 +0200 Subject: [PATCH 09/11] Local with upstream --- abi/contract.go | 9 ++++++++- abi/state_variable.go | 5 ----- ast/modifier.go | 5 ++--- ast/parameter.go | 18 ++++++++++++++---- ast/primary_expression.go | 7 +++++++ ast/reference.go | 10 ++++++---- ast/storage.go | 35 +++++++++++++++++++++++++---------- ast/tree.go | 4 ++-- ast/type_name.go | 22 +--------------------- contracts/constructor.go | 4 +++- ir/function_call.go | 4 ++++ ir/standards.go | 20 ++++++++------------ ir/variables.go | 7 +++---- parser/solidity_parser.go | 5 ++++- storage/reader.go | 6 +++--- 15 files changed, 90 insertions(+), 71 deletions(-) diff --git a/abi/contract.go b/abi/contract.go index ddb95307..3a9bd784 100644 --- a/abi/contract.go +++ b/abi/contract.go @@ -51,6 +51,11 @@ func (b *Builder) processContract(contract *ir.Contract) (*Contract, error) { // Process state variables. for _, stateVar := range contract.GetStateVariables() { + // Some old contracts will have this broken. It's related to 0.4 contracts. + // Better to have at least something then nothing at all in this point. + if stateVar.Name == "" && stateVar.GetTypeDescription() == nil { + continue + } method := b.processStateVariable(stateVar) toReturn = append(toReturn, method) } @@ -143,7 +148,9 @@ func (b *Builder) buildMethodIO(method MethodIO, typeDescr *ast.TypeDescription) } method.InternalType = typeDescr.GetString() case "struct": - return b.resolver.ResolveStructType(typeDescr) + structMember := b.resolver.ResolveStructType(typeDescr) + structMember.Name = method.Name + return structMember default: method.Type = typeName method.InternalType = typeDescr.GetString() diff --git a/abi/state_variable.go b/abi/state_variable.go index b804290d..c7818639 100644 --- a/abi/state_variable.go +++ b/abi/state_variable.go @@ -2,7 +2,6 @@ package abi import ( "github.com/unpackdev/solgo/ir" - "github.com/unpackdev/solgo/utils" ) // processStateVariable processes the provided StateVariable from the IR and constructs a Method representation. @@ -17,10 +16,6 @@ func (b *Builder) processStateVariable(stateVar *ir.StateVariable) *Method { StateMutability: b.normalizeStateMutability(stateVar.GetStateMutability()), } - if stateVar.GetTypeDescription() == nil { - utils.DumpNodeWithExit(stateVar) - } - typeName := b.resolver.ResolveType(stateVar.GetTypeDescription()) switch typeName { diff --git a/ast/modifier.go b/ast/modifier.go index 2fa1845f..fad4803f 100644 --- a/ast/modifier.go +++ b/ast/modifier.go @@ -215,11 +215,9 @@ func (m *ModifierDefinition) ParseDefinition( parameters.Src = m.Src } m.Parameters = parameters - + bodyNode := NewBodyNode(m.ASTBuilder, false) if ctx.Block() != nil && !ctx.Block().IsEmpty() { - bodyNode := NewBodyNode(m.ASTBuilder, false) bodyNode.ParseBlock(unit, contractNode, m, ctx.Block()) - m.Body = bodyNode if ctx.Block().AllUncheckedBlock() != nil { for _, uncheckedCtx := range ctx.Block().AllUncheckedBlock() { @@ -229,6 +227,7 @@ func (m *ModifierDefinition) ParseDefinition( } } } + m.Body = bodyNode m.currentModifiers = append(m.currentModifiers, m) return m diff --git a/ast/parameter.go b/ast/parameter.go index 7c2f7287..f30a2b7a 100644 --- a/ast/parameter.go +++ b/ast/parameter.go @@ -285,11 +285,21 @@ func (p *Parameter) Parse(unit *SourceUnit[Node[ast_pb.SourceUnit]], fnNode Node p.TypeDescription = typeName.GetTypeDescription() p.currentVariables = append(p.currentVariables, p) - if refId, refTypeDescription := p.GetResolver().ResolveByNode(typeName, typeName.Name); refTypeDescription != nil { - typeName.ReferencedDeclaration = refId - typeName.TypeDescription = refTypeDescription - p.TypeDescription = typeName.GetTypeDescription() + if p.TypeDescription == nil { + if refId, refTypeDescription := p.GetResolver().ResolveByNode(typeName, typeName.Name); refTypeDescription != nil { + typeName.ReferencedDeclaration = refId + typeName.TypeDescription = refTypeDescription + p.TypeDescription = typeName.GetTypeDescription() + } + } + + if p.Name == "" && p.TypeName != nil && p.TypeName.Name != "" { + p.Name = p.TypeName.Name } + + /* if p.Id == 4253 { + utils.DumpNodeWithExit(p) + }*/ } // ParseEventParameter parses the event parameter context and populates the Parameter fields for event parameters. diff --git a/ast/primary_expression.go b/ast/primary_expression.go index ba3e327f..fecf760e 100644 --- a/ast/primary_expression.go +++ b/ast/primary_expression.go @@ -217,6 +217,13 @@ func (p *PrimaryExpression) Parse( } } + if ctx.GetText() == "throw" { + p.TypeDescription = &TypeDescription{ + TypeIdentifier: "t_magic_throw", + TypeString: "throw", + } + } + if ctx.GetText() == "block" { p.TypeDescription = &TypeDescription{ TypeIdentifier: "t_magic_block", diff --git a/ast/reference.go b/ast/reference.go index 347186f6..e5cada3e 100644 --- a/ast/reference.go +++ b/ast/reference.go @@ -56,10 +56,8 @@ func (r *Resolver) ResolveByNode(node Node[NodeType], name string) (int64, *Type isPrefixSlice := strings.HasPrefix(name, "[]") cleanedName := name - if isSlice { - cleanedName = strings.TrimSuffix(name, "[]") - } else if isPrefixSlice { - cleanedName = strings.TrimPrefix(name, "[]") + if isSlice || isPrefixSlice { + cleanedName = strings.ReplaceAll(name, "[]", "") } rNode, rNodeType := r.resolveByNode(cleanedName, node) @@ -479,6 +477,10 @@ func (r *Resolver) byEvents(name string) (int64, *TypeDescription) { } func (r *Resolver) byGlobals(name string) (int64, *TypeDescription) { + if strings.Contains(name, "[]") { + name = strings.ReplaceAll(name, "[]", "") + } + for _, node := range r.globalDefinitions { switch nodeCtx := node.(type) { case *EnumDefinition: diff --git a/ast/storage.go b/ast/storage.go index 63b84907..aa265365 100644 --- a/ast/storage.go +++ b/ast/storage.go @@ -56,7 +56,7 @@ func (t *TypeName) StorageSize() (int64, bool) { if strings.Contains(t.GetTypeDescription().GetString(), "int_const") { rationalParts := strings.Split(t.GetTypeDescription().GetIdentifier(), "_by_") if len(rationalParts) == 2 { - numerator, err1 := strconv.Atoi(rationalParts[0][len(rationalParts[0])-2:]) + numerator, err1 := strconv.Atoi(strings.Replace(rationalParts[0], "t_rational_", "", -1)) denominator, err2 := strconv.Atoi(rationalParts[1]) if err1 == nil && err2 == nil { bitSize := int64(math.Ceil(math.Log2(float64(numerator / denominator)))) @@ -91,8 +91,17 @@ func elementaryTypeSizeInBits(typeName string) (int64, bool) { // `bytes` with a fixed size, and dynamically sized types like `string` and `bytes`. // Returns the size and a boolean indicating if the type is recognized. func getTypeSizeInBits(typeName string) (int64, bool) { - // TODO: Make this actually work better... Figure out dynamically what is the size of an array - typeName = strings.TrimSuffix(typeName, "[]") + // Handle array types + if strings.HasSuffix(typeName, "[]") { + elementType := strings.TrimSuffix(typeName, "[]") + elementSize, ok := getTypeSizeInBits(elementType) + if !ok { + return 0, false + } + // For dynamic arrays, the size in bits depends on the actual content + // and includes the overhead for array length (256 bits). + return elementSize + 256, true + } switch { case typeName == "bool": @@ -113,21 +122,27 @@ func getTypeSizeInBits(typeName string) (int64, bool) { } return int64(bitSize), true - + case typeName == "string": + // Dynamic-size types; the size depends on the actual content. + // It's hard to determine the exact size in bits without the content. + // Returning a default size for the pointer. + return 256, true + case typeName == "bytes": + // Dynamic-size types; the size depends on the actual content. + // It's hard to determine the exact size in bits without the content. + // Returning a default size for the pointer. + return 256, true case strings.HasPrefix(typeName, "bytes"): byteSizeStr := strings.TrimPrefix(typeName, "bytes") + if byteSizeStr == "" { + return 0, false // Dynamic-sized bytes array, which is not supported + } byteSize, err := strconv.Atoi(byteSizeStr) if err != nil || byteSize < 1 || byteSize > 32 { return 0, false } return int64(byteSize) * 8, true - case typeName == "string", typeName == "bytes": - // Dynamic-size types; the size depends on the actual content. - // It's hard to determine the exact size in bits without the content. - // Returning a default size for the pointer. - return 256, true - default: return 0, false // Type not recognized } diff --git a/ast/tree.go b/ast/tree.go index 4a1b6ac5..07c0a541 100644 --- a/ast/tree.go +++ b/ast/tree.go @@ -70,13 +70,13 @@ func (t *Tree) UpdateNodeReferenceById(nodeId int64, nodeRefId int64, typeRef *T return false } - for _, child := range t.astRoot.GetNodes() { + for _, child := range t.astRoot.GetGlobalNodes() { if n := t.byRecursiveReferenceUpdate(child, nodeId, nodeRefId, typeRef); n { return n } } - for _, child := range t.astRoot.GetGlobalNodes() { + for _, child := range t.astRoot.GetNodes() { if n := t.byRecursiveReferenceUpdate(child, nodeId, nodeRefId, typeRef); n { return n } diff --git a/ast/type_name.go b/ast/type_name.go index 1c5193f6..eaff87b3 100644 --- a/ast/type_name.go +++ b/ast/type_name.go @@ -75,30 +75,10 @@ func (t *TypeName) SetReferenceDescriptor(refId int64, refDesc *TypeDescription) if strings.HasSuffix(t.Name, "[]") && refDesc != nil { if !strings.HasSuffix(refDesc.TypeString, "[]") { t.TypeDescription.TypeString += "[]" - /*if t.PathNode != nil && t.PathNode.TypeDescription != nil { - if !strings.HasSuffix(t.PathNode.Name, "[]") { - if strings.HasSuffix(t.PathNode.TypeDescription.TypeString, "[]") { - // Kumbayaa through fucking recursion... - t.PathNode.TypeDescription.TypeString = strings.TrimSuffix( - t.PathNode.TypeDescription.TypeString, "[]", - ) - } - } - }*/ + return true } } - // Lets update the parent node as well in case that type description is not set... - /* parentNodeId := t.GetSrc().GetParentIndex() - - if parentNodeId > 0 { - if parentNode := t.GetTree().GetById(parentNodeId); parentNode != nil { - if parentNode.GetTypeDescription() == nil { - parentNode.SetReferenceDescriptor(refId, refDesc) - } - } - } - */ return true } diff --git a/contracts/constructor.go b/contracts/constructor.go index 99991d66..dc4a74e5 100644 --- a/contracts/constructor.go +++ b/contracts/constructor.go @@ -29,6 +29,7 @@ func (c *Contract) DiscoverConstructor(ctx context.Context) error { abiRoot != nil && abiRoot.GetEntryContract().GetMethodByType("constructor") != nil { cAbi, _ := utils.ToJSON(abiRoot.GetEntryContract().GetMethodByType("constructor")) constructorAbi := fmt.Sprintf("[%s]", string(cAbi)) + //utils.DumpNodeWithExit(abiRoot.GetEntryContract().GetMethodByType("constructor")) //utils.DumpNodeWithExit(irRoot.GetEntryContract().GetConstructor().GetParameters()) tx := c.descriptor.Transaction deployedBytecode := c.descriptor.DeployedBytecode @@ -50,7 +51,8 @@ func (c *Contract) DiscoverConstructor(ctx context.Context) error { // TODO: Fix // - 0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936 (239 bytes more) - Some shitty text... - + //spew.Dump(hex.EncodeToString(tx.Data())) + //fmt.Println("") //spew.Dump(hex.EncodeToString(adjustedData[constructorDataIndex:])) //spew.Dump(constructorAbi) // 239 //utils.DumpNodeWithExit(irRoot.GetEntryContract().GetConstructor().GetParameters()) diff --git a/ir/function_call.go b/ir/function_call.go index c185ce28..d23eedcf 100644 --- a/ir/function_call.go +++ b/ir/function_call.go @@ -1,6 +1,7 @@ package ir import ( + "github.com/unpackdev/solgo/utils" "strings" v3 "github.com/cncf/xds/go/xds/type/v3" @@ -139,6 +140,9 @@ func (b *Builder) processFunctionCall(fn *Function, unit *ast.FunctionCall) *Fun } for _, arg := range unit.GetArguments() { + if arg.GetTypeDescription() == nil { + utils.DumpNodeWithExit(arg) + } toReturn.ArgumentTypes = append(toReturn.ArgumentTypes, arg.GetTypeDescription().ToProto()) } diff --git a/ir/standards.go b/ir/standards.go index 9e72d08a..bcff441e 100644 --- a/ir/standards.go +++ b/ir/standards.go @@ -3,7 +3,6 @@ package ir import ( ir_pb "github.com/unpackdev/protos/dist/go/ir" "github.com/unpackdev/solgo/standards" - "github.com/unpackdev/solgo/utils" ) // Standard represents a specific Ethereum Improvement Proposal standard that a contract may adhere to. @@ -70,6 +69,9 @@ func (b *Builder) processEips(root *RootSourceUnit) { outputs := make([]standards.Output, 0) for _, param := range function.GetParameters() { + if param.GetTypeDescription() == nil { + //utils.DumpNodeWithExit(param) + } inputs = append(inputs, standards.Input{ Type: param.GetTypeDescription().GetString(), Indexed: false, // Specific to events... @@ -77,18 +79,12 @@ func (b *Builder) processEips(root *RootSourceUnit) { } for _, ret := range function.GetReturnStatements() { - if ret.GetTypeDescription() != nil { - outputs = append(outputs, standards.Output{ - Type: "t_unknown", // Will fix this later on with upgrade of parser to support solidity 0.5+ - }) - } else { - if ret.GetTypeDescription() == nil { - utils.DumpNodeWithExit(function) - } - outputs = append(outputs, standards.Output{ - Type: ret.GetTypeDescription().GetString(), - }) + if ret.GetTypeDescription() == nil { + //utils.DumpNodeWithExit(function) } + outputs = append(outputs, standards.Output{ + Type: ret.GetTypeDescription().GetString(), + }) } contract.Functions = append(contract.Functions, standards.StandardFunction{ diff --git a/ir/variables.go b/ir/variables.go index c5cd5b2b..6ceac67f 100644 --- a/ir/variables.go +++ b/ir/variables.go @@ -1,7 +1,6 @@ package ir import ( - "github.com/unpackdev/solgo/utils" "strings" ast_pb "github.com/unpackdev/protos/dist/go/ast" @@ -128,9 +127,9 @@ func (b *Builder) processStateVariables(unit *ast.StateVariableDeclaration) *Sta // It could be that the name of the type name node is not set, but the type description string is. if variableNode.Type == "" { - if variableNode.TypeDescription == nil { - utils.DumpNodeWithExit(variableNode) - } + /* if variableNode.TypeDescription == nil { + utils.DumpNodeWithExit(variableNode) + }*/ variableNode.Type = variableNode.TypeDescription.TypeString } diff --git a/parser/solidity_parser.go b/parser/solidity_parser.go index e66e2be9..2f67aea5 100644 --- a/parser/solidity_parser.go +++ b/parser/solidity_parser.go @@ -25049,11 +25049,14 @@ func (p *SolidityParser) Sempred(localctx antlr.RuleContext, ruleIndex, predInde case 30: var t *StateVariableDeclarationContext = nil if localctx != nil { - if tCtx, ok := localctx.(*StateVariableDeclarationContext); ok { + if tCtx, ok := localctx.(*StateVariableDeclarationContext); ok { t = tCtx } else if _, ok := localctx.(*IdentifierPathContext); ok { return true + } else if _, ok := localctx.(*TypeNameContext); ok { + return true } + //fmt.Printf("RECEIVED LOCALCLX %T \n", localctx) } return p.StateVariableDeclaration_Sempred(t, predIndex) diff --git a/storage/reader.go b/storage/reader.go index 3dd02f56..9a03b437 100644 --- a/storage/reader.go +++ b/storage/reader.go @@ -3,7 +3,6 @@ package storage import ( "context" "fmt" - "github.com/unpackdev/solgo/utils" "strings" ) @@ -75,8 +74,9 @@ func (r *Reader) CalculateStorageLayout() error { for _, variable := range variables { storageSize, found := variable.GetAST().GetTypeName().StorageSize() if !found { - utils.DumpNodeWithExit(variable.GetAST().GetTypeName()) - return fmt.Errorf("error calculating storage size for variable: %s", variable.GetName()) + //utils.DumpNodeNoExit(variable.GetAST().GetTypeName()) + //return fmt.Errorf("error calculating storage size for variable: %s", variable.GetName()) + continue } typeName := variable.GetType() From 75a7a6dce2eb2b5abeef52c5f2d2edffa5a11cb4 Mon Sep 17 00:00:00 2001 From: Nevio Date: Thu, 1 Aug 2024 11:20:08 +0200 Subject: [PATCH 10/11] Untested fully signature computation --- ast/function.go | 98 ++++++++++++++++++++++++++++++++++++++++++--- ir/function.go | 7 ++++ ir/function_call.go | 7 ++-- 3 files changed, 103 insertions(+), 9 deletions(-) diff --git a/ast/function.go b/ast/function.go index e035d226..c3088c97 100644 --- a/ast/function.go +++ b/ast/function.go @@ -151,7 +151,74 @@ func (f *Function) ComputeSignature() { if f.GetParameters() != nil { for _, param := range f.GetParameters().GetParameters() { - params = append(params, param.TypeName.Name) + typeString := param.TypeName.TypeDescription.TypeString + actualParam := param.TypeName.Name + + if strings.Contains(typeString, "contract") { + if strings.Contains(typeString, "[]") { + actualParam = "address[]" + } else { + actualParam = "address" + } + } else if strings.Contains(typeString, "struct") { + structParams := make([]string, 0) + if iNode := f.resolver.GetTree().GetById(param.TypeName.GetReferencedDeclaration()); iNode != nil { + switch node := iNode.(type) { + case *Parameter: + if piNode := f.resolver.GetTree().GetById(node.GetTypeName().GetReferencedDeclaration()); piNode != nil { + switch pNode := piNode.(type) { + case *StructDefinition: + for _, member := range pNode.GetMembers() { + if strings.Contains(member.GetTypeDescription().GetString(), "contract") { + if strings.Contains(member.GetTypeDescription().GetString(), "[]") { + structParams = append(structParams, "address[]") + } else { + structParams = append(structParams, "address") + } + } else if strings.Contains(member.GetTypeDescription().GetString(), "enum") { + if strings.Contains(member.GetTypeDescription().GetString(), "[]") { + structParams = append(structParams, "uint8[]") + } else { + structParams = append(structParams, "uint8") + } + } else { + structParams = append(structParams, member.GetTypeDescription().GetString()) + } + } + } + } + case *StructDefinition: + for _, member := range node.GetMembers() { + if strings.Contains(member.GetTypeDescription().GetString(), "contract") { + if strings.Contains(member.GetTypeDescription().GetString(), "[]") { + structParams = append(structParams, "address[]") + } else { + structParams = append(structParams, "address") + } + } else if strings.Contains(member.GetTypeDescription().GetString(), "enum") { + if strings.Contains(member.GetTypeDescription().GetString(), "[]") { + structParams = append(structParams, "uint8[]") + } else { + structParams = append(structParams, "uint8") + } + } else { + structParams = append(structParams, member.GetTypeDescription().GetString()) + } + } + } + } + actualParam = "("+strings.Join(structParams, ",")+")" + } else if strings.Contains(typeString, "enum") { + if strings.Contains(typeString, "[]") { + actualParam = "uint8[]" + } else { + actualParam = "uint8" + } + } else if strings.Contains(typeString, "mapping") { + actualParam = strings.Join(translateMapping(typeString), ",") + } + + params = append(params, actualParam) } } f.SignatureRaw = strings.Join( @@ -492,8 +559,6 @@ func (f *Function) Parse( f.TypeDescription = f.buildTypeDescription() - f.ComputeSignature() - f.currentFunctions = append(f.currentFunctions, f) return f @@ -556,8 +621,6 @@ func (f *Function) ParseTypeName( f.TypeDescription = f.buildTypeDescription() - f.ComputeSignature() - f.currentFunctions = append(f.currentFunctions, f) return f @@ -676,3 +739,28 @@ func (f *Function) getVirtualState(ctx *parser.FunctionDefinitionContext) bool { return false } + + +// translateMapping translates a Solidity mapping type string into a slice of data types. +func translateMapping(mappingType string) []string { + var result []string + + // Check if the input string starts with "mapping(" + if !strings.HasPrefix(mappingType, "mapping(") { + return result + } + + // Remove the "mapping(" prefix and the closing ")" at the end + mappingType = strings.TrimPrefix(mappingType, "mapping(") + mappingType = strings.TrimSuffix(mappingType, ")") + + // Split the string by "=>" + parts := strings.Split(mappingType, "=>") + for _, part := range parts { + // Remove any leading and trailing spaces + part = strings.TrimSpace(part) + result = append(result, part) + } + + return result +} \ No newline at end of file diff --git a/ir/function.go b/ir/function.go index 017f618b..334c1083 100644 --- a/ir/function.go +++ b/ir/function.go @@ -153,6 +153,13 @@ func (f *Function) ToProto() *ir_pb.Function { // processFunction processes the function declaration and returns the Function. func (b *Builder) processFunction(unit *ast.Function, parseBody bool) *Function { + + // First, we are going to compute signature itself... + // This is not done at the time of AST function build because reference declaration searches + // are not completed so type names will be missing for many functions until reference search is completed. + // This is, for now, expensive function as it iterates through references in case struct is found... + unit.ComputeSignature() + toReturn := &Function{ Unit: unit, Id: unit.GetId(), diff --git a/ir/function_call.go b/ir/function_call.go index d23eedcf..0f3a81b6 100644 --- a/ir/function_call.go +++ b/ir/function_call.go @@ -1,7 +1,6 @@ package ir import ( - "github.com/unpackdev/solgo/utils" "strings" v3 "github.com/cncf/xds/go/xds/type/v3" @@ -140,9 +139,9 @@ func (b *Builder) processFunctionCall(fn *Function, unit *ast.FunctionCall) *Fun } for _, arg := range unit.GetArguments() { - if arg.GetTypeDescription() == nil { - utils.DumpNodeWithExit(arg) - } + /* if arg.GetTypeDescription() == nil { + utils.DumpNodeWithExit(arg) + }*/ toReturn.ArgumentTypes = append(toReturn.ArgumentTypes, arg.GetTypeDescription().ToProto()) } From 3fdd85994e4a7e02aace2bee27e4948b179b17f9 Mon Sep 17 00:00:00 2001 From: Nevio Date: Fri, 30 Aug 2024 10:35:59 +0200 Subject: [PATCH 11/11] Random changes --- abi/contract.go | 6 +- abi/state_variable.go | 4 + accounts/account.go | 3 +- ast/function.go | 38 ++++---- ast/visitor.go | 4 + bindings/manager.go | 2 +- bindings/simulator.go | 66 ++++++++++++++ bindings/token.go | 120 ++++++++++++++++++++++++ bindings/trace.go | 2 - bindings/transactor.go | 137 ++++++++++++++++++++++++++++ bindings/uniswap_v2.go | 137 +++++++++++++++++++++++++++- contracts/source.go | 12 ++- providers/etherscan/contract.go | 2 +- providers/etherscan/transactions.go | 4 + tokens/bindings.go | 5 + utils/addr.go | 4 + utils/types.go | 12 +++ 17 files changed, 530 insertions(+), 28 deletions(-) create mode 100644 bindings/simulator.go create mode 100644 bindings/transactor.go diff --git a/abi/contract.go b/abi/contract.go index 3a9bd784..fa6b8def 100644 --- a/abi/contract.go +++ b/abi/contract.go @@ -56,8 +56,10 @@ func (b *Builder) processContract(contract *ir.Contract) (*Contract, error) { if stateVar.Name == "" && stateVar.GetTypeDescription() == nil { continue } - method := b.processStateVariable(stateVar) - toReturn = append(toReturn, method) + + if method := b.processStateVariable(stateVar); method != nil { + toReturn = append(toReturn, method) + } } // Process events. diff --git a/abi/state_variable.go b/abi/state_variable.go index c7818639..1385c3e7 100644 --- a/abi/state_variable.go +++ b/abi/state_variable.go @@ -16,6 +16,10 @@ func (b *Builder) processStateVariable(stateVar *ir.StateVariable) *Method { StateMutability: b.normalizeStateMutability(stateVar.GetStateMutability()), } + if stateVar.GetTypeDescription() == nil { + return nil + } + typeName := b.resolver.ResolveType(stateVar.GetTypeDescription()) switch typeName { diff --git a/accounts/account.go b/accounts/account.go index 15c4196d..e893004e 100644 --- a/accounts/account.go +++ b/accounts/account.go @@ -75,8 +75,7 @@ func (a *Account) GetClient() *clients.Client { // This method is mainly used for testing purposes in simulation environments like Anvil. // It does not affect the real balance on the Ethereum network. func (a *Account) SetAccountBalance(ctx context.Context, amount *big.Int) error { - amountHex := common.Bytes2Hex(amount.Bytes()) - return a.client.GetRpcClient().CallContext(ctx, nil, "anvil_setBalance", a.GetAddress(), amountHex) + return a.client.GetRpcClient().CallContext(ctx, nil, "anvil_setBalance", a.GetAddress(), amount.String()) } // Balance retrieves the account's balance from the Ethereum network at a specified block number. diff --git a/ast/function.go b/ast/function.go index c3088c97..e2b62f0c 100644 --- a/ast/function.go +++ b/ast/function.go @@ -151,6 +151,9 @@ func (f *Function) ComputeSignature() { if f.GetParameters() != nil { for _, param := range f.GetParameters().GetParameters() { + /* if param.TypeName.TypeDescription == nil { + utils.DumpNodeWithExit(param) + }*/ typeString := param.TypeName.TypeDescription.TypeString actualParam := param.TypeName.Name @@ -187,27 +190,27 @@ func (f *Function) ComputeSignature() { } } } - case *StructDefinition: - for _, member := range node.GetMembers() { - if strings.Contains(member.GetTypeDescription().GetString(), "contract") { - if strings.Contains(member.GetTypeDescription().GetString(), "[]") { - structParams = append(structParams, "address[]") - } else { - structParams = append(structParams, "address") - } - } else if strings.Contains(member.GetTypeDescription().GetString(), "enum") { - if strings.Contains(member.GetTypeDescription().GetString(), "[]") { - structParams = append(structParams, "uint8[]") - } else { - structParams = append(structParams, "uint8") - } + case *StructDefinition: + for _, member := range node.GetMembers() { + if strings.Contains(member.GetTypeDescription().GetString(), "contract") { + if strings.Contains(member.GetTypeDescription().GetString(), "[]") { + structParams = append(structParams, "address[]") } else { - structParams = append(structParams, member.GetTypeDescription().GetString()) + structParams = append(structParams, "address") } + } else if strings.Contains(member.GetTypeDescription().GetString(), "enum") { + if strings.Contains(member.GetTypeDescription().GetString(), "[]") { + structParams = append(structParams, "uint8[]") + } else { + structParams = append(structParams, "uint8") + } + } else { + structParams = append(structParams, member.GetTypeDescription().GetString()) } + } } } - actualParam = "("+strings.Join(structParams, ",")+")" + actualParam = "(" + strings.Join(structParams, ",") + ")" } else if strings.Contains(typeString, "enum") { if strings.Contains(typeString, "[]") { actualParam = "uint8[]" @@ -740,7 +743,6 @@ func (f *Function) getVirtualState(ctx *parser.FunctionDefinitionContext) bool { return false } - // translateMapping translates a Solidity mapping type string into a slice of data types. func translateMapping(mappingType string) []string { var result []string @@ -763,4 +765,4 @@ func translateMapping(mappingType string) []string { } return result -} \ No newline at end of file +} diff --git a/ast/visitor.go b/ast/visitor.go index ae709578..b74a8bf5 100644 --- a/ast/visitor.go +++ b/ast/visitor.go @@ -1,6 +1,7 @@ package ast import ( + "github.com/pkg/errors" ast_pb "github.com/unpackdev/protos/dist/go/ast" ) @@ -49,6 +50,9 @@ func (t *Tree) WalkNodes(startNodes []Node[NodeType], visitor *NodeVisitor) erro // ExecuteTypeVisit executes a visitation function on all nodes of a specific type in the AST, starting from the root. func (t *Tree) ExecuteTypeVisit(nodeType ast_pb.NodeType, visitFunc func(node Node[NodeType]) (bool, error)) (bool, error) { + if t.astRoot == nil || t.astRoot.GetNodes() == nil { + return false, errors.New("cannot execute type visit on empty tree") + } return t.executeTypeVisitRecursive(t.astRoot.GetNodes(), nodeType, visitFunc) } diff --git a/bindings/manager.go b/bindings/manager.go index 5a0ba7d0..25a793e7 100644 --- a/bindings/manager.go +++ b/bindings/manager.go @@ -40,6 +40,7 @@ func NewManager(ctx context.Context, clientPool *clients.ClientPool) (*Manager, ctx: ctx, clientPool: clientPool, bindings: make(map[utils.Network]map[BindingType]*Binding), + mu: sync.RWMutex{}, }, nil } @@ -297,7 +298,6 @@ func (m *Manager) CallContractMethodUnpackMap(ctx context.Context, network utils return unpackedResults, nil } - // CallContractMethodUnpackMap executes a contract method call and unpacks the results into a map, providing // a flexible interface for handling contract outputs. func (m *Manager) CallContractMethodUnpackMapAtBlock(ctx context.Context, network utils.Network, blockNumber *big.Int, bindingType BindingType, toAddr common.Address, methodName string, params ...interface{}) (map[string]any, error) { diff --git a/bindings/simulator.go b/bindings/simulator.go new file mode 100644 index 00000000..e0da12af --- /dev/null +++ b/bindings/simulator.go @@ -0,0 +1,66 @@ +package bindings + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/unpackdev/solgo/clients" + "github.com/unpackdev/solgo/utils" +) + +type JSONRPCRequest struct { + JSONRPC string `json:"jsonrpc"` + Method string `json:"method"` + Params []interface{} `json:"params"` + ID int `json:"id"` +} + +func (m *Manager) ImpersonateAccount(network utils.Network, contract common.Address) (common.Address, error) { + client := m.clientPool.GetClientByGroup(string(network)) + if client == nil { + return contract, fmt.Errorf("client not found for network %s", network) + } + + rpcClient := client.GetRpcClient() + if err := rpcClient.Call(nil, "anvil_impersonateAccount", contract.Hex()); err != nil { + return contract, fmt.Errorf("failed to impersonate account: %v", err) + } + + return contract, nil +} + +func (m *Manager) StopImpersonateAccount(network utils.Network, contract common.Address) (common.Address, error) { + client := m.clientPool.GetClientByGroup(string(network)) + if client == nil { + return contract, fmt.Errorf("client not found for network %s", network) + } + + rpcClient := client.GetRpcClient() + if err := rpcClient.Call(nil, "anvil_stopImpersonatingAccount", contract.Hex()); err != nil { + return contract, fmt.Errorf("failed to stop impersonating account: %v", err) + } + + return contract, nil +} + +func (m *Manager) SendSimulatedTransaction(opts *bind.TransactOpts, network utils.Network, simulatorType utils.SimulatorType, client *clients.Client, contract *common.Address, method abi.Method, input []byte) (*common.Hash, error) { + txArgs := map[string]interface{}{ + "from": opts.From.Hex(), + "to": contract.Hex(), + "data": hexutil.Encode(input), // method + arguments... + } + + if opts.Value != nil { + txArgs["value"] = hexutil.EncodeBig(opts.Value) + } + + var txHash common.Hash + if err := client.GetRpcClient().Call(&txHash, "eth_sendTransaction", txArgs); err != nil { + return nil, fmt.Errorf("failed to send transaction: %v - contract: %v - args: %v", err, contract.Hex(), txArgs) + } + + return &txHash, nil +} diff --git a/bindings/token.go b/bindings/token.go index 3219d048..ef331d44 100644 --- a/bindings/token.go +++ b/bindings/token.go @@ -3,6 +3,11 @@ package bindings import ( "context" "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/core/types" + "github.com/pkg/errors" + "github.com/unpackdev/solgo/clients" + "go.uber.org/zap" "math/big" "github.com/ethereum/go-ethereum/common" @@ -212,6 +217,121 @@ func (t *Token) GetOptionsByNetwork(network utils.Network) *BindOptions { return nil } +func (t *Token) Transfer(ctx context.Context, network utils.Network, simulatorType utils.SimulatorType, client *clients.Client, opts *bind.TransactOpts, to common.Address, amount *big.Int, atBlock *big.Int) (*types.Transaction, *types.Receipt, error) { + binding, err := t.GetBinding(utils.Ethereum, Erc20) + if err != nil { + return nil, nil, err + } + bindingAbi := binding.GetABI() + + method, exists := bindingAbi.Methods["transfer"] + if !exists { + return nil, nil, errors.New("transfer method not found") + } + + select { + case <-ctx.Done(): + return nil, nil, ctx.Err() + default: + input, err := bindingAbi.Pack(method.Name, to, amount) + if err != nil { + return nil, nil, err + } + + tx, err := t.Manager.SendTransaction(opts, t.network, simulatorType, client, &binding.Address, input) + if err != nil { + return nil, nil, fmt.Errorf("failed to send transfer transaction: %w", err) + } + + receipt, err := t.Manager.WaitForReceipt(t.ctx, network, simulatorType, client, tx.Hash()) + if err != nil { + return nil, nil, fmt.Errorf("failed to get transfer transaction receipt: %w", err) + } + + return tx, receipt, nil + } +} + +func (t *Token) Approve(ctx context.Context, network utils.Network, simulatorType utils.SimulatorType, client *clients.Client, opts *bind.TransactOpts, from common.Address, spender common.Address, amount *big.Int, atBlock *big.Int) (*types.Transaction, *types.Receipt, error) { + binding, err := t.GetBinding(utils.Ethereum, Erc20) + if err != nil { + return nil, nil, err + } + bindingAbi := binding.GetABI() + + method, exists := bindingAbi.Methods["approve"] + if !exists { + return nil, nil, errors.New("approve method not found") + } + + input, err := bindingAbi.Pack(method.Name, spender, amount) + if err != nil { + return nil, nil, err + } + + select { + case <-ctx.Done(): + return nil, nil, ctx.Err() + default: + tx, err := t.Manager.SendTransaction(opts, t.network, simulatorType, client, &from, input) + if err != nil { + return nil, nil, fmt.Errorf("failed to send approve transaction: %w", err) + } + + receipt, err := t.Manager.WaitForReceipt(t.ctx, network, simulatorType, client, tx.Hash()) + if err != nil { + return nil, nil, fmt.Errorf("failed to get approve transaction receipt: %w", err) + } + + zap.L().Debug( + "Approve transaction sent and receipt received", + zap.String("tx_hash", tx.Hash().Hex()), + zap.String("tx_from", spender.Hex()), + zap.String("tx_to", tx.To().Hex()), + zap.String("tx_nonce", fmt.Sprintf("%d", tx.Nonce())), + zap.String("tx_gas_price", tx.GasPrice().String()), + zap.String("tx_gas", fmt.Sprintf("%d", tx.Gas())), + ) + + return tx, receipt, nil + } +} + +func (t *Token) TransferFrom(ctx context.Context, network utils.Network, simulatorType utils.SimulatorType, client *clients.Client, opts *bind.TransactOpts, from, to common.Address, amount *big.Int, atBlock *big.Int) (*types.Transaction, *types.Receipt, error) { + binding, err := t.GetBinding(utils.Ethereum, Erc20) + if err != nil { + return nil, nil, err + } + bindingAbi := binding.GetABI() + + method, exists := bindingAbi.Methods["transferFrom"] + if !exists { + return nil, nil, errors.New("transfer method not found") + } + + input, err := bindingAbi.Pack(method.Name, from, to, amount) + if err != nil { + return nil, nil, err + } + + select { + case <-ctx.Done(): + return nil, nil, ctx.Err() + default: + tx, err := t.Manager.SendTransaction(opts, t.network, simulatorType, client, &binding.Address, input) + if err != nil { + return nil, nil, fmt.Errorf("failed to send transfer transaction: %w", err) + } + + receipt, err := t.Manager.WaitForReceipt(t.ctx, network, simulatorType, client, tx.Hash()) + if err != nil { + return nil, nil, fmt.Errorf("failed to get transfer transaction receipt: %w", err) + } + + return tx, receipt, nil + } +} + // DefaultTokenBindOptions generates a default set of BindOptions for ERC20 and ERC20Ownable tokens. It presets // configurations such as networks, network IDs, types, contract addresses, and ABIs based on standard // implementations. This function simplifies the setup process for common token types. diff --git a/bindings/trace.go b/bindings/trace.go index cdae71e5..49448573 100644 --- a/bindings/trace.go +++ b/bindings/trace.go @@ -55,10 +55,8 @@ func (m *Manager) TraceBlock(ctx context.Context, network utils.Network, number if err := rpcClient.CallContext(ctx, &result, "trace_block", numberHex); err != nil { if err := rpcClient.CallContext(ctx, &tmp, "trace_block", numberHex); err != nil { - fmt.Println(err) return nil, errors.Wrap(err, "failed to execute trace_block") } - //utils.DumpNodeWithExit(tmp) return nil, errors.Wrap(err, "failed to execute trace_block") } diff --git a/bindings/transactor.go b/bindings/transactor.go new file mode 100644 index 00000000..37198d03 --- /dev/null +++ b/bindings/transactor.go @@ -0,0 +1,137 @@ +package bindings + +import ( + "context" + "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/pkg/errors" + "github.com/unpackdev/solgo/clients" + "github.com/unpackdev/solgo/utils" + "math/big" + "time" +) + +var ( + // These values should be set based on current network conditions. + // 2 Gwei is often a reasonable default for priority fee. + defaultMaxPriorityFeePerGas = big.NewInt(2e9) // 2 Gwei + +) + +func (m *Manager) GetTransactionByHash(ctx context.Context, network utils.Network, txHash common.Hash) (*types.Transaction, bool, error) { + client := m.clientPool.GetClientByGroup(string(network)) + if client == nil { + return nil, false, fmt.Errorf("client not found for network %s", network) + } + + return client.TransactionByHash(ctx, txHash) +} + +func (m *Manager) WaitForReceipt(ctx context.Context, network utils.Network, simulatorType utils.SimulatorType, client *clients.Client, txHash common.Hash) (*types.Receipt, error) { + // TODO: This should be configurable per network... (this: 60 seconds) + ctxWait, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + for { + select { + case <-ctxWait.Done(): + return nil, fmt.Errorf("context cancelled while waiting to get transaction receipt: %s", txHash.Hex()) + default: + receipt, err := client.TransactionReceipt(ctxWait, txHash) + if err != nil { + if errors.Is(err, context.DeadlineExceeded) { + return nil, fmt.Errorf("timeout waiting to get transaction receipt: %s", txHash.Hex()) + } + // Transaction not yet mined + time.Sleep(500 * time.Millisecond) // Configurable delay + continue + } + + return receipt, nil + } + } +} + +func (m *Manager) SendTransaction(opts *bind.TransactOpts, network utils.Network, simulateType utils.SimulatorType, client *clients.Client, contract *common.Address, input []byte) (*types.Transaction, error) { + var rawTx *types.Transaction + var err error + if opts.GasPrice != nil { + rawTx, err = createLegacyTx(opts, contract, input) + } else { + var head *types.Header + var errHead error + + head, errHead = client.HeaderByNumber(opts.Context, nil) + if errHead != nil { + return nil, errHead + } + + if head.BaseFee != nil { + rawTx, err = createDynamicTx(opts, contract, input, head) + } else { + rawTx, err = createLegacyTx(opts, contract, input) + } + } + if err != nil { + return nil, err + } + + if opts.Signer == nil { + return nil, fmt.Errorf( + "no signer to authorize the transaction with, network: %s, simulate_type: %s, contract: %s", + network, simulateType, contract.Hex(), + ) + } + + signedTx, err := opts.Signer(opts.From, rawTx) + if err != nil { + return nil, err + } + + if opts.NoSend { + return signedTx, nil + } + + if err := client.SendTransaction(opts.Context, signedTx); err != nil { + return nil, err + } + + return signedTx, nil +} + +func createLegacyTx(opts *bind.TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) { + // Create a legacy transaction + tx := types.NewTransaction(opts.Nonce.Uint64(), *contract, opts.Value, opts.GasLimit, opts.GasPrice, input) + return tx, nil +} + +func createDynamicTx(opts *bind.TransactOpts, contract *common.Address, input []byte, head *types.Header) (*types.Transaction, error) { + // Calculate the effective gas fee cap and tip cap + gasFeeCap := opts.GasFeeCap + gasTipCap := opts.GasTipCap + + if gasFeeCap == nil { + // Set default max fee per gas if not provided + gasFeeCap = new(big.Int).Add(head.BaseFee, defaultMaxPriorityFeePerGas) + } + + if gasTipCap == nil { + // Set default priority fee if not provided + gasTipCap = defaultMaxPriorityFeePerGas + } + + // Create a dynamic fee transaction (EIP-1559) + tx := types.NewTx(&types.DynamicFeeTx{ + ChainID: big.NewInt(int64(utils.EthereumNetworkID)), + Nonce: opts.Nonce.Uint64(), + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Gas: opts.GasLimit, + To: contract, + Value: opts.Value, + Data: input, + }) + return tx, nil +} diff --git a/bindings/uniswap_v2.go b/bindings/uniswap_v2.go index 8b5d587e..e558f96f 100644 --- a/bindings/uniswap_v2.go +++ b/bindings/uniswap_v2.go @@ -3,8 +3,14 @@ package bindings import ( "context" "fmt" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/pkg/errors" + "github.com/unpackdev/solgo/clients" "github.com/unpackdev/solgo/utils" + "github.com/unpackdev/solgo/utils/entities" "math/big" ) @@ -33,6 +39,10 @@ func NewUniswapV2(ctx context.Context, network utils.Network, manager *Manager, opts = DefaultUniswapV2BindOptions() } + if manager == nil { + return nil, errors.New("binding manager is not set while creating new uniswap_v2 bindings") + } + for _, opt := range opts { if err := opt.Validate(); err != nil { return nil, err @@ -41,7 +51,13 @@ func NewUniswapV2(ctx context.Context, network utils.Network, manager *Manager, for _, opt := range opts { for _, oNetwork := range opt.Networks { - if _, err := manager.RegisterBinding(oNetwork, opt.NetworkID, opt.Type, opt.Address, opt.ABI); err != nil { + if _, err := manager.RegisterBinding( + oNetwork, + opt.NetworkID, + opt.Type, + opt.Address, + opt.ABI, + ); err != nil { return nil, err } } @@ -143,6 +159,125 @@ func (u *UniswapV2) GetAmountOut(ctx context.Context, amountIn *big.Int, reserve return amountOut, nil } +func (u *UniswapV2) Buy(opts *bind.TransactOpts, network utils.Network, simulatorType utils.SimulatorType, client *clients.Client, amountOutMin *big.Int, path []common.Address, to common.Address, amount *entities.CurrencyAmount, deadline *big.Int) (*types.Transaction, *types.Receipt, error) { + bind, err := u.GetBinding(utils.Ethereum, UniswapV2Router) + if err != nil { + return nil, nil, err + } + bindAbi := bind.GetABI() + + method, exists := bindAbi.Methods["swapExactETHForTokens"] + if !exists { + return nil, nil, errors.New("swapExactETHForTokens method not found") + } + + input, err := bindAbi.Pack(method.Name, amountOutMin, path, to, deadline) + if err != nil { + return nil, nil, err + } + + // Estimate gas limit + gasLimit, err := client.EstimateGas(context.Background(), ethereum.CallMsg{ + From: opts.From, + To: &bind.Address, + Data: input, + Value: amount.Quotient(), + }) + if err != nil { + return nil, nil, fmt.Errorf("failed to estimate gas: %w", err) + } + opts.GasLimit = gasLimit + + // Set gas price or fees if not already set + if opts.GasPrice == nil && opts.GasFeeCap == nil && opts.GasTipCap == nil { + head, err := client.HeaderByNumber(context.Background(), nil) + if err != nil { + return nil, nil, fmt.Errorf("failed to get latest block header: %w", err) + } + + if head.BaseFee != nil { + // EIP-1559 transaction (dynamic fee) + opts.GasFeeCap = new(big.Int).Add(head.BaseFee, defaultMaxPriorityFeePerGas) + opts.GasTipCap = defaultMaxPriorityFeePerGas + } else { + // Legacy transaction + opts.GasPrice = big.NewInt(2e9) // 2 Gwei + } + } + + tx, err := u.Manager.SendTransaction(opts, u.network, simulatorType, client, &bind.Address, input) + if err != nil { + return nil, nil, fmt.Errorf("failed to send swapExactETHForTokens transaction: %w", err) + } + + receipt, err := u.Manager.WaitForReceipt(u.ctx, network, simulatorType, client, tx.Hash()) + if err != nil { + return nil, nil, fmt.Errorf("failed to get swapExactETHForTokens transaction receipt: %w", err) + } + + return tx, receipt, nil +} + +func (u *UniswapV2) Sell(opts *bind.TransactOpts, network utils.Network, simulatorType utils.SimulatorType, client *clients.Client, amountIn *big.Int, amountOutMin *big.Int, path []common.Address, to common.Address, deadline *big.Int) (*types.Transaction, *types.Receipt, error) { + bind, err := u.GetBinding(utils.Ethereum, UniswapV2Router) + if err != nil { + return nil, nil, err + } + + bindAbi := bind.GetABI() + + method, exists := bindAbi.Methods["swapExactTokensForETHSupportingFeeOnTransferTokens"] + if !exists { + return nil, nil, errors.New("swapExactTokensForETH method not found") + } + + input, err := bindAbi.Pack(method.Name, amountIn, amountOutMin, path, to, deadline) + if err != nil { + return nil, nil, err + } + + // Estimate gas limit + gasLimit, err := client.EstimateGas(context.Background(), ethereum.CallMsg{ + From: opts.From, + To: &bind.Address, + Data: input, + //Value: amountIn, + }) + if err != nil { + return nil, nil, fmt.Errorf("failed to estimate gas: %w", err) + } + opts.GasLimit = gasLimit + + // Set gas price or fees if not already set + if opts.GasPrice == nil && opts.GasFeeCap == nil && opts.GasTipCap == nil { + head, err := client.HeaderByNumber(context.Background(), nil) + if err != nil { + return nil, nil, fmt.Errorf("failed to get latest block header: %w", err) + } + + if head.BaseFee != nil { + // EIP-1559 transaction (dynamic fee) + opts.GasFeeCap = new(big.Int).Add(head.BaseFee, defaultMaxPriorityFeePerGas) + opts.GasTipCap = defaultMaxPriorityFeePerGas + } else { + // Legacy transaction + opts.GasPrice = big.NewInt(2e9) // 2 Gwei + } + } + + tx, err := u.Manager.SendTransaction(opts, u.network, simulatorType, client, &bind.Address, input) + if err != nil { + return nil, nil, fmt.Errorf("failed to send swapExactTokensForETH transaction: %w", err) + } + + receipt, err := u.Manager.WaitForReceipt(u.ctx, network, simulatorType, client, tx.Hash()) + if err != nil { + return nil, nil, fmt.Errorf("failed to get swapExactTokensForETH transaction receipt: %w", err) + } + + return tx, receipt, nil +} + func DefaultUniswapV2BindOptions() []*BindOptions { return []*BindOptions{ { diff --git a/contracts/source.go b/contracts/source.go index 777869c6..0b370970 100644 --- a/contracts/source.go +++ b/contracts/source.go @@ -32,7 +32,7 @@ func (c *Contract) DiscoverSourceCode(ctx context.Context) error { // Retry mechanism const maxRetries = 15 for i := 0; i < maxRetries; i++ { - dCtx, dCancel := context.WithTimeout(ctx, 15*time.Second) + dCtx, dCancel := context.WithTimeout(ctx, 60*time.Second) response, err = c.etherscan.ScanContract(dCtx, c.addr) if err != nil { if strings.Contains(err.Error(), "Max rate limit reached") || @@ -41,6 +41,11 @@ func (c *Contract) DiscoverSourceCode(ctx context.Context) error { time.Sleep(time.Duration(i*1000) * time.Millisecond) dCancel() continue + } else if strings.Contains(err.Error(), "Max calls per sec rate limit reached") { + // Wait for i*1000ms before retrying + time.Sleep(time.Duration(i*1000) * time.Millisecond) + dCancel() + continue } else if strings.Contains(err.Error(), "invalid character") { // Wait for i*1000ms before retrying time.Sleep(time.Duration(i*100) * time.Millisecond) @@ -61,6 +66,11 @@ func (c *Contract) DiscoverSourceCode(ctx context.Context) error { time.Sleep(time.Duration(i*100) * time.Millisecond) dCancel() continue + } else if strings.Contains(err.Error(), "TLS handshake timeout") { + // Wait for i*1000ms before retrying + time.Sleep(time.Duration(i*100) * time.Millisecond) + dCancel() + continue } else if !strings.Contains(err.Error(), "not found") && !strings.Contains(err.Error(), "not verified") { zap.L().Error( diff --git a/providers/etherscan/contract.go b/providers/etherscan/contract.go index 2190751e..a3f0695f 100644 --- a/providers/etherscan/contract.go +++ b/providers/etherscan/contract.go @@ -122,7 +122,7 @@ func (e *Provider) ScanContract(ctx context.Context, addr common.Address) (*Cont if err := json.Unmarshal(body, &contractResponse); err != nil { return nil, fmt.Errorf("failed to unmarshal error response: %s", err) } - return nil, errors.New(contractResponse.Result) + return nil, fmt.Errorf("%s - %s", contractResponse.Result, url) } var contractResponse ContractResponse diff --git a/providers/etherscan/transactions.go b/providers/etherscan/transactions.go index 80d76595..2c868040 100644 --- a/providers/etherscan/transactions.go +++ b/providers/etherscan/transactions.go @@ -102,5 +102,9 @@ func (e *Provider) QueryContractCreationTx(ctx context.Context, addr common.Addr } } + if creationResponse.Result == nil || len(creationResponse.Result) == 0 { + return nil, fmt.Errorf("failed to find contract creation response for addr: %s", addr.Hex()) + } + return creationResponse.Result[0], nil } diff --git a/tokens/bindings.go b/tokens/bindings.go index b3dcd716..9c2e8905 100644 --- a/tokens/bindings.go +++ b/tokens/bindings.go @@ -19,6 +19,11 @@ func (t *Token) PrepareBindings(ctx context.Context) error { return err } +// GetUniswapV2Bind ... +func (t *Token) GetUniswapV2Bind(bindManager *bindings.Manager) (*bindings.UniswapV2, error) { + return bindings.NewUniswapV2(t.ctx, t.network, bindManager, bindings.DefaultUniswapV2BindOptions()) +} + // GetTokenBind connects to a blockchain simulator or live network to create a token binding. // It uses bindings.Manager and can target a specific block number. func (t *Token) GetTokenBind(ctx context.Context, bindManager *bindings.Manager) (*bindings.Token, error) { diff --git a/utils/addr.go b/utils/addr.go index a0f002e9..0cbfeb5c 100644 --- a/utils/addr.go +++ b/utils/addr.go @@ -36,6 +36,10 @@ func (n NamedAddr) AppendTags(tags ...string) NamedAddr { return n } +func (n NamedAddr) Hex() string { + return n.Addr.Hex() +} + // IsZeroAddress checks whenever named address is zero address func (n NamedAddr) IsZeroAddress() bool { return n.Addr == ZeroAddress diff --git a/utils/types.go b/utils/types.go index c867d24c..f12dff4e 100644 --- a/utils/types.go +++ b/utils/types.go @@ -112,6 +112,14 @@ func (t BlacklistType) String() string { return string(t) } +// TransferType defines the type of transfer, including "account" and "contract". +type TransferType string + +// String returns the string representation of a TradeType. +func (t TransferType) String() string { + return string(t) +} + // Constants defining various strategies, address types, transaction method types, etc. const ( HeadStrategy Strategy = "head" @@ -126,6 +134,10 @@ const ( ContractAddressType AddressType = "contract" + AccountTransferType TransferType = "account" + ContractTransferType TransferType = "contract" + + UnknownTransactionMethodType TransactionMethodType = "unknown" ContractCreationType TransactionMethodType = "contract_creation" ApproveMethodType TransactionMethodType = "approve"