Skip to content

Commit

Permalink
still in a wip state
Browse files Browse the repository at this point in the history
  • Loading branch information
jwasinger committed Nov 20, 2024
1 parent a2479e1 commit 46e6dd4
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 250 deletions.
16 changes: 16 additions & 0 deletions accounts/abi/bind/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,18 @@ func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend Co
return c.address, tx, c, nil
}

func DeployContractRaw(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, packedParams []byte) (common.Address, *types.Transaction, *BoundContract, error) {
// Otherwise try to deploy the contract
c := NewBoundContract(common.Address{}, abi, backend, backend, backend)

tx, err := c.transact(opts, nil, append(bytecode, packedParams...))
if err != nil {
return common.Address{}, nil, nil, err
}
c.address = crypto.CreateAddress(opts.From, tx.Nonce())
return c.address, tx, c, nil
}

// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
Expand Down Expand Up @@ -179,6 +191,10 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
return c.abi.UnpackIntoInterface(res[0], method, output)
}

func (c *BoundContract) CallRaw(opts *CallOpts, input []byte) ([]byte, error) {
return c.call(opts, input)
}

func (c *BoundContract) call(opts *CallOpts, input []byte) ([]byte, error) {
// Don't crash on a lazy user
if opts == nil {
Expand Down
72 changes: 57 additions & 15 deletions accounts/abi/bind/bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,17 +312,19 @@ func bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
}

contracts[types[i]] = &tmplContract{
Type: capitalise(types[i]),
InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""),
InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
Constructor: evmABI.Constructor,
Calls: calls,
Transacts: transacts,
Fallback: fallback,
Receive: receive,
Events: events,
Libraries: make(map[string]string),
Type: capitalise(types[i]),
InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""),
InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
Constructor: evmABI.Constructor,
Calls: calls,
Transacts: transacts,
Fallback: fallback,
Receive: receive,
Events: events,
Libraries: make(map[string]string),
AllLibraries: make(map[string]string),
}

// Function 4-byte signatures are stored in the same sequence
// as types, if available.
if len(fsigs) > i {
Expand All @@ -340,14 +342,54 @@ func bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
if _, ok := isLib[name]; !ok {
isLib[name] = struct{}{}
}

}
}
// Check if that type has already been identified as a library
for i := 0; i < len(types); i++ {
_, ok := isLib[types[i]]
contracts[types[i]].Library = ok
}

// recursively traverse the library dependency graph
// of the contract, flattening it into a list.
//
// For abigenv2, we do not generate contract deploy
// methods (which in v1 recursively deploy their
// library dependencies). So, the entire set of
// library dependencies is required, and we will
// the order to deploy and link them at runtime.
var findDeps func(contract *tmplContract) map[string]struct{}
findDeps = func(contract *tmplContract) map[string]struct{} {
// 1) match all libraries that this contract depends on
re, err := regexp.Compile("__\\$([a-f0-9]+)\\$__")
if err != nil {
panic(err)
}
libBin := contracts[contract.Type].InputBin
matches := re.FindAllStringSubmatch(libBin, -1)
var result map[string]struct{}

// 2) recurse, gathering nested library dependencies
for _, match := range matches {
pattern := match[1]
result[pattern] = struct{}{}
depContract := contracts[pattern]
for subPattern, _ := range findDeps(depContract) {
result[subPattern] = struct{}{}
}
}
return result
}
// take the set of library patterns, convert it to a map of pattern -> type
deps := findDeps(contracts[types[i]])
contracts[types[i]].AllLibraries = make(map[string]string)
for contractPattern, _ := range deps {
contractType := libs[contractPattern]
contracts[types[i]].AllLibraries[contractType] = contractPattern
}
}
// Check if that type has already been identified as a library
for i := 0; i < len(types); i++ {
_, ok := isLib[types[i]]
contracts[types[i]].Library = ok
}

// Generate the contract template data content and render it
data := &tmplData{
Package: pkg,
Expand Down
8 changes: 1 addition & 7 deletions accounts/abi/bind/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package bind

import (
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
)

Expand All @@ -30,10 +29,5 @@ type ContractInstance interface {

type ContractInstanceV2 interface {
Address() common.Address
}

func CallRaw(instance ContractInstance, opts *CallOpts, input []byte) ([]byte, error) {
backend := instance.Backend()
c := NewBoundContract(instance.Address(), abi.ABI{}, backend, backend, backend)
return c.call(opts, input)
Backend() ContractBackend
}
25 changes: 13 additions & 12 deletions accounts/abi/bind/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,19 @@ type tmplData struct {

// tmplContract contains the data needed to generate an individual contract binding.
type tmplContract struct {
Type string // Type name of the main contract binding
InputABI string // JSON ABI used as the input to generate the binding from
InputBin string // Optional EVM bytecode used to generate deploy code from
FuncSigs map[string]string // Optional map: string signature -> 4-byte signature
Constructor abi.Method // Contract constructor for deploy parametrization
Calls map[string]*tmplMethod // Contract calls that only read state data
Transacts map[string]*tmplMethod // Contract calls that write state data
Fallback *tmplMethod // Additional special fallback function
Receive *tmplMethod // Additional special receive function
Events map[string]*tmplEvent // Contract events accessors
Libraries map[string]string // Same as tmplData, but filtered to only keep what the contract needs
Library bool // Indicator whether the contract is a library
Type string // Type name of the main contract binding
InputABI string // JSON ABI used as the input to generate the binding from
InputBin string // Optional EVM bytecode used to generate deploy code from
FuncSigs map[string]string // Optional map: string signature -> 4-byte signature
Constructor abi.Method // Contract constructor for deploy parametrization
Calls map[string]*tmplMethod // Contract calls that only read state data
Transacts map[string]*tmplMethod // Contract calls that write state data
Fallback *tmplMethod // Additional special fallback function
Receive *tmplMethod // Additional special receive function
Events map[string]*tmplEvent // Contract events accessors
Libraries map[string]string // Same as tmplData, but filtered to only keep direct deps that the contract needs
AllLibraries map[string]string // same as Libraries, but all direct/indirect library dependencies
Library bool // Indicator whether the contract is a library
}

// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
Expand Down
23 changes: 12 additions & 11 deletions accounts/abi/bind/template2.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 44 additions & 0 deletions accounts/abi/bind/testdata/v2_testcase_library/contract.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

library RecursiveDep {
function AddOne(uint256 val) public pure returns (uint256 ret) {
return val + 1;
}
}

// Array function to delete element at index and re-organize the array
// so that there are no gaps between the elements.
library Array {
using RecursiveDep for uint256;

function remove(uint256[] storage arr, uint256 index) public {
// Move the last element into the place to delete
require(arr.length > 0, "Can't remove from empty array");
arr[index] = arr[arr.length - 1];
arr[index] = arr[index].AddOne();
arr.pop();
}
}

contract TestArray {
using Array for uint256[];

uint256[] public arr;

function testArrayRemove(uint256 value) public {
for (uint256 i = 0; i < 3; i++) {
arr.push(i);
}

arr.remove(1);

assert(arr.length == 2);
assert(arr[0] == 0);
assert(arr[1] == 2);
}

constructor(uint256 foobar) {

}
}
Loading

0 comments on commit 46e6dd4

Please sign in to comment.