Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
32eea1b6
Unverified
Commit
32eea1b6
authored
Aug 16, 2024
by
protolambda
Committed by
GitHub
Aug 17, 2024
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
op-chain-ops: automatic ABI bindings from Go struct (#11497)
parent
a81de910
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
384 additions
and
55 deletions
+384
-55
bindings.go
op-chain-ops/script/bindings.go
+187
-0
bindings_test.go
op-chain-ops/script/bindings_test.go
+101
-0
precompile.go
op-chain-ops/script/precompile.go
+96
-55
No files found.
op-chain-ops/script/bindings.go
0 → 100644
View file @
32eea1b6
package
script
import
(
"errors"
"fmt"
"reflect"
"strings"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/core/vm"
)
// CallBackendFn is the function that encoded binding calls get made with.
// The function may return vm.ErrExecutionReverted to revert (revert is ABI decoded from data).
// Or any other error, where the return-data is then ignored.
type
CallBackendFn
func
(
data
[]
byte
)
([]
byte
,
error
)
// MakeBindings turns a struct type with function-typed fields into EVM call bindings
// that are hooked up to the backend function.
// fields annotated with `evm:"-"` are ignored.
func
MakeBindings
[
E
any
](
backendFn
CallBackendFn
,
checkABI
func
(
abiDef
string
)
bool
,
)
(
*
E
,
error
)
{
v
:=
new
(
E
)
val
:=
reflect
.
ValueOf
(
v
)
err
:=
hydrateBindingsStruct
(
val
,
backendFn
,
checkABI
)
return
v
,
err
}
// hydrateBindingsStruct initializes a struct with function fields into
// a struct of ABI functions hooked up to the backend.
func
hydrateBindingsStruct
(
val
reflect
.
Value
,
backendFn
CallBackendFn
,
checkABI
func
(
abiDef
string
)
bool
,
)
error
{
typ
:=
val
.
Type
()
if
typ
.
Kind
()
==
reflect
.
Pointer
{
if
val
.
IsNil
()
{
return
errors
.
New
(
"cannot hydrate nil pointer value"
)
}
val
=
val
.
Elem
()
typ
=
val
.
Type
()
}
if
typ
.
Kind
()
!=
reflect
.
Struct
{
return
fmt
.
Errorf
(
"object is not a struct: %s"
,
typ
)
}
// Hydrate each of the fields
fieldCount
:=
val
.
NumField
()
for
i
:=
0
;
i
<
fieldCount
;
i
++
{
fieldDef
:=
typ
.
Field
(
i
)
if
!
fieldDef
.
IsExported
()
{
// ignore unexposed fields
continue
}
// ignore fields / embedded structs that are annotated with `evm:"-"`
if
v
,
ok
:=
fieldDef
.
Tag
.
Lookup
(
"evm"
);
ok
&&
v
==
"-"
{
continue
}
if
fieldDef
.
Anonymous
{
// fields of embedded structs will be hydrated too
if
err
:=
hydrateBindingsStruct
(
val
.
Field
(
i
),
backendFn
,
checkABI
);
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to hydrate bindings of embedded field %q: %w"
,
fieldDef
.
Name
,
err
)
}
continue
}
if
fieldDef
.
Type
.
Kind
()
!=
reflect
.
Func
{
// We can only hydrate fields with a function type
continue
}
fVal
:=
val
.
Field
(
i
)
if
!
fVal
.
IsNil
()
{
return
fmt
.
Errorf
(
"cannot hydrate bindings func, field %q is already set"
,
fieldDef
.
Name
)
}
if
err
:=
hydrateBindingsField
(
fVal
,
fieldDef
,
backendFn
,
checkABI
);
err
!=
nil
{
return
fmt
.
Errorf
(
"cannot hydrate bindings field %q: %w"
,
fieldDef
.
Name
,
err
)
}
}
return
nil
}
var
ErrABICheck
=
errors
.
New
(
"failed ABI check"
)
// hydrateBindingsField initializes a struct field value to a function that calls the implied ABI function.
func
hydrateBindingsField
(
fVal
reflect
.
Value
,
fieldDef
reflect
.
StructField
,
backendFn
CallBackendFn
,
checkABI
func
(
abiDef
string
)
bool
,
)
error
{
// derive the ABI function name from the field name
abiFunctionName
:=
fieldDef
.
Name
if
len
(
abiFunctionName
)
==
0
{
return
errors
.
New
(
"ABI method name must not be empty"
)
}
if
lo
:=
strings
.
ToLower
(
abiFunctionName
[
:
1
]);
lo
!=
abiFunctionName
[
:
1
]
{
abiFunctionName
=
lo
+
abiFunctionName
[
1
:
]
}
// derive the ABI function arguments from the function type
inArgs
,
err
:=
makeArgs
(
fieldDef
.
Type
.
NumIn
(),
fieldDef
.
Type
.
In
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to determine ABI types of input args: %w"
,
err
)
}
inArgTypes
:=
makeArgTypes
(
inArgs
)
methodSig
:=
fmt
.
Sprintf
(
"%v(%v)"
,
abiFunctionName
,
strings
.
Join
(
inArgTypes
,
","
))
// check the ABI, if we can
if
checkABI
!=
nil
{
if
!
checkABI
(
methodSig
)
{
return
fmt
.
Errorf
(
"function %s with signature %q is invalid: %w"
,
fieldDef
.
Name
,
methodSig
,
ErrABICheck
)
}
}
byte4Sig
:=
bytes4
(
methodSig
)
// Define how we encode Go arguments as function calldata, including the function selector
inArgsEncodeFn
:=
func
(
args
[]
reflect
.
Value
)
([]
byte
,
error
)
{
vals
:=
make
([]
interface
{},
len
(
args
))
for
i
:=
range
args
{
vals
[
i
]
=
args
[
i
]
.
Interface
()
}
out
,
err
:=
inArgs
.
PackValues
(
vals
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to encode call data: %w"
,
err
)
}
calldata
:=
make
([]
byte
,
0
,
len
(
out
)
+
4
)
calldata
=
append
(
calldata
,
byte4Sig
[
:
]
...
)
calldata
=
append
(
calldata
,
out
...
)
return
calldata
,
nil
}
// Determine how many arguments we decode from ABI, and if we have an error return case.
outArgCount
:=
fieldDef
.
Type
.
NumOut
()
errReturn
:=
hasTrailingError
(
outArgCount
,
fieldDef
.
Type
.
Out
)
var
nilErrValue
reflect
.
Value
if
errReturn
{
outArgCount
-=
1
nilErrValue
=
reflect
.
New
(
fieldDef
.
Type
.
Out
(
outArgCount
))
.
Elem
()
}
outArgs
,
err
:=
makeArgs
(
outArgCount
,
fieldDef
.
Type
.
Out
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to determine ABI types of output args: %w"
,
err
)
}
outAllocators
:=
makeArgAllocators
(
outArgCount
,
fieldDef
.
Type
.
Out
)
// Helper func to return an error with, where we try to fit it in the returned error value, if there is any.
returnErr
:=
func
(
err
error
)
[]
reflect
.
Value
{
if
!
errReturn
{
panic
(
fmt
.
Errorf
(
"error, but cannot return as arg: %w"
,
err
))
}
out
:=
make
([]
reflect
.
Value
,
outArgCount
+
1
)
for
i
:=
0
;
i
<
outArgCount
;
i
++
{
out
[
i
]
=
reflect
.
New
(
fieldDef
.
Type
.
Out
(
i
))
.
Elem
()
}
out
[
outArgCount
]
=
reflect
.
ValueOf
(
err
)
return
out
}
// Decodes the result of the backend into values to return as function, including error/revert handling.
outDecodeFn
:=
func
(
result
[]
byte
,
resultErr
error
)
[]
reflect
.
Value
{
if
resultErr
!=
nil
{
if
errors
.
Is
(
resultErr
,
vm
.
ErrExecutionReverted
)
{
msg
,
err
:=
abi
.
UnpackRevert
(
result
)
if
err
!=
nil
{
return
returnErr
(
fmt
.
Errorf
(
"failed to unpack result args: %w"
,
err
))
}
return
returnErr
(
fmt
.
Errorf
(
"revert: %s"
,
msg
))
}
return
returnErr
(
resultErr
)
}
out
:=
make
([]
reflect
.
Value
,
outArgCount
,
outArgCount
+
1
)
err
:=
abiToValues
(
outArgs
,
outAllocators
,
out
,
result
)
if
err
!=
nil
{
return
returnErr
(
fmt
.
Errorf
(
"failed to convert output to return values: %w"
,
err
))
}
if
errReturn
{
// don't forget the nil error value, to match the expected output arg count
out
=
append
(
out
,
nilErrValue
)
}
return
out
}
// Construct the actual Go function: it encodes the Go args, turns it into an ABI call, and decodes the results.
f
:=
reflect
.
MakeFunc
(
fieldDef
.
Type
,
func
(
args
[]
reflect
.
Value
)
(
results
[]
reflect
.
Value
)
{
input
,
err
:=
inArgsEncodeFn
(
args
)
// ABI encode args
if
err
!=
nil
{
return
returnErr
(
fmt
.
Errorf
(
"failed to encode input args: %w"
,
err
))
}
result
,
err
:=
backendFn
(
input
)
// call backend func
return
outDecodeFn
(
result
,
err
)
// ABI decode result
})
// Now hydrate the field definition with our new Go function
fVal
.
Set
(
f
)
return
nil
}
op-chain-ops/script/bindings_test.go
0 → 100644
View file @
32eea1b6
package
script
import
(
"errors"
"fmt"
"math/big"
"testing"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
)
type
EmbeddedBindings
struct
{
Adder
func
(
a
uint8
,
b
uint64
)
*
big
.
Int
}
type
ExampleBindings
struct
{
DoThing
func
()
error
EmbeddedBindings
Hello
func
(
greeting
string
,
target
common
.
Address
)
string
}
func
TestBindings
(
t
*
testing
.
T
)
{
var
testFn
CallBackendFn
backendFn
:=
func
(
data
[]
byte
)
([]
byte
,
error
)
{
return
testFn
(
data
)
// indirect call, so we can swap it per test case.
}
bindings
,
err
:=
MakeBindings
[
ExampleBindings
](
backendFn
,
nil
)
require
.
NoError
(
t
,
err
)
testFn
=
func
(
data
[]
byte
)
([]
byte
,
error
)
{
require
.
Len
(
t
,
data
,
4
)
require
.
Equal
(
t
,
bytes4
(
"doThing()"
),
[
4
]
byte
(
data
))
return
encodeRevert
(
errors
.
New
(
"example revert"
))
}
err
=
bindings
.
DoThing
()
require
.
ErrorContains
(
t
,
err
,
"example revert"
)
testFn
=
func
(
data
[]
byte
)
([]
byte
,
error
)
{
require
.
Len
(
t
,
data
,
4
)
require
.
Equal
(
t
,
bytes4
(
"doThing()"
),
[
4
]
byte
(
data
))
return
[]
byte
{},
nil
}
err
=
bindings
.
DoThing
()
require
.
NoError
(
t
,
err
,
"no revert"
)
testFn
=
func
(
data
[]
byte
)
([]
byte
,
error
)
{
require
.
Len
(
t
,
data
,
4
+
32
+
32
,
"selector and two ABI args"
)
require
.
Equal
(
t
,
bytes4
(
"adder(uint8,uint64)"
),
[
4
]
byte
(
data
[
:
4
]))
a
:=
new
(
big
.
Int
)
.
SetBytes
(
data
[
4
:
4
+
32
])
b
:=
new
(
big
.
Int
)
.
SetBytes
(
data
[
4
+
32
:
])
return
leftPad32
(
new
(
big
.
Int
)
.
Add
(
a
,
b
)
.
Bytes
()),
nil
}
result
:=
bindings
.
Adder
(
42
,
0x1337
)
require
.
NoError
(
t
,
err
)
require
.
True
(
t
,
result
.
IsUint64
())
require
.
Equal
(
t
,
uint64
(
42
+
0x1337
),
result
.
Uint64
())
}
type
TestContract
struct
{}
func
(
e
*
TestContract
)
Hello
(
greeting
string
,
target
common
.
Address
)
string
{
return
fmt
.
Sprintf
(
"Test says: %s %s!"
,
greeting
,
target
)
}
func
TestPrecompileBindings
(
t
*
testing
.
T
)
{
contract
,
err
:=
NewPrecompile
[
*
TestContract
](
&
TestContract
{})
require
.
NoError
(
t
,
err
)
bindings
,
err
:=
MakeBindings
[
ExampleBindings
](
contract
.
Run
,
nil
)
require
.
NoError
(
t
,
err
)
addr
:=
common
.
HexToAddress
(
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
)
response
:=
bindings
.
Hello
(
"Hola"
,
addr
)
require
.
Equal
(
t
,
fmt
.
Sprintf
(
"Test says: Hola %s!"
,
addr
),
response
)
}
func
TestBindingsABICheck
(
t
*
testing
.
T
)
{
fn
:=
CallBackendFn
(
func
(
data
[]
byte
)
([]
byte
,
error
)
{
panic
(
"should not run"
)
})
needABI
:=
map
[
string
]
struct
{}{
"doThing()"
:
{},
"adder(uint8,uint64)"
:
{},
"hello(string,address)"
:
{},
}
gotABI
:=
make
(
map
[
string
]
struct
{})
abiCheckFn
:=
func
(
abiDef
string
)
bool
{
_
,
ok
:=
needABI
[
abiDef
]
gotABI
[
abiDef
]
=
struct
{}{}
return
ok
}
_
,
err
:=
MakeBindings
[
ExampleBindings
](
fn
,
abiCheckFn
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
needABI
,
gotABI
,
"checked all ABI methods"
)
delete
(
needABI
,
"doThing()"
)
_
,
err
=
MakeBindings
[
ExampleBindings
](
fn
,
abiCheckFn
)
require
.
ErrorIs
(
t
,
err
,
ErrABICheck
)
}
op-chain-ops/script/precompile.go
View file @
32eea1b6
...
...
@@ -99,6 +99,53 @@ func (p *Precompile[E]) setupMethods(val *reflect.Value) error {
return
nil
}
// makeArgs infers a list of ABI types, from a list of Go arguments.
func
makeArgs
(
argCount
int
,
getType
func
(
i
int
)
reflect
.
Type
)
(
abi
.
Arguments
,
error
)
{
out
:=
make
(
abi
.
Arguments
,
argCount
)
for
i
:=
0
;
i
<
argCount
;
i
++
{
argType
:=
getType
(
i
)
abiTyp
,
err
:=
goTypeToABIType
(
argType
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to determine ABI type of input arg %d: %w"
,
i
,
err
)
}
out
[
i
]
=
abi
.
Argument
{
Name
:
fmt
.
Sprintf
(
"arg_%d"
,
i
),
Type
:
abiTyp
,
}
}
return
out
,
nil
}
// makeArgTypes turns a slice of ABI argument types into a slice of ABI stringified types
func
makeArgTypes
(
args
abi
.
Arguments
)
[]
string
{
out
:=
make
([]
string
,
len
(
args
))
for
i
:=
0
;
i
<
len
(
args
);
i
++
{
out
[
i
]
=
args
[
i
]
.
Type
.
String
()
}
return
out
}
// makeArgAllocators returns a lice of Go object allocator functions, for each of the arguments.
func
makeArgAllocators
(
argCount
int
,
getType
func
(
i
int
)
reflect
.
Type
)
[]
func
()
any
{
out
:=
make
([]
func
()
interface
{},
argCount
)
for
i
:=
0
;
i
<
argCount
;
i
++
{
argType
:=
getType
(
i
)
out
[
i
]
=
func
()
interface
{}
{
return
reflect
.
New
(
argType
)
.
Elem
()
.
Interface
()
}
}
return
out
}
// hasTrailingError checks if the last returned argument type, if any, is a Go error.
func
hasTrailingError
(
argCount
int
,
getType
func
(
i
int
)
reflect
.
Type
)
bool
{
if
argCount
==
0
{
return
false
}
lastTyp
:=
getType
(
argCount
-
1
)
return
lastTyp
.
Kind
()
==
reflect
.
Interface
&&
lastTyp
.
Implements
(
typeFor
[
error
]())
}
// setupMethod takes a method definition, attached to selfVal,
// and builds an ABI method to handle the input decoding and output encoding around the inner Go function.
func
(
p
*
Precompile
[
E
])
setupMethod
(
selfVal
*
reflect
.
Value
,
methodDef
*
reflect
.
Method
)
error
{
...
...
@@ -124,24 +171,14 @@ func (p *Precompile[E]) setupMethod(selfVal *reflect.Value, methodDef *reflect.M
if
inArgCount
<
0
{
return
errors
.
New
(
"expected method with receiver as first argument"
)
}
inArgs
:=
make
(
abi
.
Arguments
,
inArgCount
)
inArgTypes
:=
make
([]
string
,
inArgCount
)
inArgAllocators
:=
make
([]
func
()
interface
{},
inArgCount
)
for
i
:=
0
;
i
<
inArgCount
;
i
++
{
argType
:=
methodDef
.
Type
.
In
(
i
+
1
)
// account for receiver
abiTyp
,
err
:=
goTypeToABIType
(
argType
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to determine ABI type of input arg %d: %w"
,
i
,
err
)
getInArg
:=
func
(
i
int
)
reflect
.
Type
{
return
methodDef
.
Type
.
In
(
i
+
1
)
// +1 to account for the receiver
}
inArgs
[
i
]
=
abi
.
Argument
{
Name
:
fmt
.
Sprintf
(
"in_%d"
,
i
),
Type
:
abiTyp
,
}
inArgAllocators
[
i
]
=
func
()
interface
{}
{
return
reflect
.
New
(
argType
)
.
Elem
()
.
Interface
()
}
inArgTypes
[
i
]
=
abiTyp
.
String
()
inArgs
,
err
:=
makeArgs
(
inArgCount
,
getInArg
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to preserve input args: %w"
,
err
)
}
inArgTypes
:=
makeArgTypes
(
inArgs
)
methodSig
:=
fmt
.
Sprintf
(
"%v(%v)"
,
abiFunctionName
,
strings
.
Join
(
inArgTypes
,
","
))
byte4Sig
:=
bytes4
(
methodSig
)
if
variantSuffix
!=
""
{
...
...
@@ -157,29 +194,18 @@ func (p *Precompile[E]) setupMethod(selfVal *reflect.Value, methodDef *reflect.M
outArgCount
:=
methodDef
.
Type
.
NumOut
()
// A Go method may return an error, which we do not ABI-encode, but rather forward as revert.
errReturn
:=
false
if
outArgCount
>
0
{
errIndex
:=
outArgCount
-
1
lastTyp
:=
methodDef
.
Type
.
Out
(
errIndex
)
if
lastTyp
.
Kind
()
==
reflect
.
Interface
&&
lastTyp
.
Implements
(
typeFor
[
error
]())
{
errReturn
:=
hasTrailingError
(
outArgCount
,
methodDef
.
Type
.
Out
)
if
errReturn
{
outArgCount
-=
1
errReturn
=
true
}
}
// Prepare ABI definitions of return parameters.
outArgs
:=
make
(
abi
.
Arguments
,
outArgCount
)
for
i
:=
0
;
i
<
outArgCount
;
i
++
{
argType
:=
methodDef
.
Type
.
Out
(
i
)
abiTyp
,
err
:=
goTypeToABIType
(
argType
)
outArgs
,
err
:=
makeArgs
(
outArgCount
,
methodDef
.
Type
.
Out
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to determine ABI type of output arg %d: %w"
,
i
,
err
)
}
outArgs
[
i
]
=
abi
.
Argument
{
Name
:
fmt
.
Sprintf
(
"out_%d"
,
i
),
Type
:
abiTyp
,
}
return
fmt
.
Errorf
(
"failed to prepare output arg types: %w"
,
err
)
}
inArgAllocators
:=
makeArgAllocators
(
inArgCount
,
getInArg
)
fn
:=
makeFn
(
selfVal
,
&
methodDef
.
Func
,
errReturn
,
inArgs
,
outArgs
,
inArgAllocators
)
p
.
abiMethods
[
byte4Sig
]
=
&
precompileFunc
{
...
...
@@ -190,34 +216,49 @@ func (p *Precompile[E]) setupMethod(selfVal *reflect.Value, methodDef *reflect.M
return
nil
}
//
makeFn is a helper function to perform a method call:
//
- ABI decoding of input
//
- type conversion of inputs
// - actual function Go call
// - handling of error return value
// - and ABI encoding of outputs
func
makeFn
(
selfVal
,
methodVal
*
reflect
.
Value
,
errReturn
bool
,
inArgs
,
outArgs
abi
.
Arguments
,
inArgAllocators
[]
func
()
any
)
func
(
input
[]
byte
)
([]
byte
,
error
)
{
return
func
(
input
[]
byte
)
([]
byte
,
error
)
{
//
abiToValues turns serialized ABI input data into values, which are written to the given dest slice.
//
The ABI decoding happens following the given args ABI type definitions.
//
Values are allocated with the given respective allocator functions.
func
abiToValues
(
args
abi
.
Arguments
,
allocators
[]
func
()
any
,
dest
[]
reflect
.
Value
,
input
[]
byte
)
error
{
// sanity check that we have as many allocators as result destination slots
if
len
(
allocators
)
!=
len
(
dest
)
{
return
fmt
.
Errorf
(
"have %d allocators, but %d destinations"
,
len
(
allocators
),
len
(
dest
))
}
// Unpack the ABI data into default Go types
inVals
,
err
:=
inA
rgs
.
UnpackValues
(
input
)
inVals
,
err
:=
a
rgs
.
UnpackValues
(
input
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to decode input: %x
\n
err: %w"
,
input
,
err
)
return
fmt
.
Errorf
(
"failed to decode input: %x
\n
err: %w"
,
input
,
err
)
}
// Sanity check that the ABI util returned the expected number of inputs
if
len
(
inVals
)
!=
len
(
inArgA
llocators
)
{
return
nil
,
fmt
.
Errorf
(
"expected %d args, got %d"
,
len
(
inArgA
llocators
),
len
(
inVals
))
if
len
(
inVals
)
!=
len
(
a
llocators
)
{
return
fmt
.
Errorf
(
"expected %d args, got %d"
,
len
(
a
llocators
),
len
(
inVals
))
}
// Convert each default Go type into the expected opinionated Go type
callArgs
:=
make
([]
reflect
.
Value
,
0
,
1
+
len
(
inArgAllocators
))
callArgs
=
append
(
callArgs
,
*
selfVal
)
for
i
,
inAlloc
:=
range
inArgAllocators
{
for
i
,
inAlloc
:=
range
allocators
{
argSrc
:=
inVals
[
i
]
argDest
:=
inAlloc
()
argDest
,
err
=
convertType
(
argSrc
,
argDest
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to convert arg %d from Go type %T to %T: %w"
,
i
,
argSrc
,
argDest
,
err
)
return
fmt
.
Errorf
(
"failed to convert arg %d from Go type %T to %T: %w"
,
i
,
argSrc
,
argDest
,
err
)
}
callArgs
=
append
(
callArgs
,
reflect
.
ValueOf
(
argDest
))
dest
[
i
]
=
reflect
.
ValueOf
(
argDest
)
}
return
nil
}
// makeFn is a helper function to perform a method call:
// - ABI decoding of input
// - type conversion of inputs
// - actual function Go call
// - handling of error return value
// - and ABI encoding of outputs
func
makeFn
(
selfVal
,
methodVal
*
reflect
.
Value
,
errReturn
bool
,
inArgs
,
outArgs
abi
.
Arguments
,
inArgAllocators
[]
func
()
any
)
func
(
input
[]
byte
)
([]
byte
,
error
)
{
return
func
(
input
[]
byte
)
([]
byte
,
error
)
{
// Convert each default Go type into the expected opinionated Go type
callArgs
:=
make
([]
reflect
.
Value
,
1
+
len
(
inArgAllocators
))
callArgs
[
0
]
=
*
selfVal
err
:=
abiToValues
(
inArgs
,
inArgAllocators
,
callArgs
[
1
:
],
input
)
if
err
!=
nil
{
return
nil
,
err
}
// Call the precompile Go function
returnReflectVals
:=
methodVal
.
Call
(
callArgs
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment