Skip to content

Commit

Permalink
Add EVM methods for encodeABI & decodeABI
Browse files Browse the repository at this point in the history
  • Loading branch information
m-Peter committed Dec 3, 2023
1 parent 87380a4 commit 0b25342
Show file tree
Hide file tree
Showing 3 changed files with 438 additions and 0 deletions.
10 changes: 10 additions & 0 deletions fvm/evm/stdlib/contract.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,14 @@ contract EVM {
fun run(tx: [UInt8], coinbase: EVMAddress) {
InternalEVM.run(tx: tx, coinbase: coinbase.bytes)
}

access(all)
fun encodeABI(_ values: [AnyStruct]): [UInt8] {
return InternalEVM.encodeABI(values)
}

access(all)
fun decodeABI(_ types: [Type], data: [UInt8]): [AnyStruct] {
return InternalEVM.decodeABI(types, data: data)
}
}
212 changes: 212 additions & 0 deletions fvm/evm/stdlib/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"regexp"

gethABI "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/onflow/cadence"
"github.com/onflow/cadence/runtime"
"github.com/onflow/cadence/runtime/common"
Expand Down Expand Up @@ -38,6 +39,203 @@ var evmAddressBytesType = sema.NewConstantSizedType(nil, sema.UInt8Type, types.A
var evmAddressBytesStaticType = interpreter.ConvertSemaArrayTypeToStaticArrayType(nil, evmAddressBytesType)
var EVMAddressBytesCadenceType = cadence.NewConstantSizedArrayType(types.AddressLength, cadence.TheUInt8Type)

// EVM.encodeABI

const internalEVMTypeEncodeABIFunctionName = "encodeABI"

var internalEVMTypeEncodeABIFunctionType = &sema.FunctionType{
Parameters: []sema.Parameter{
{
Label: sema.ArgumentLabelNotRequired,
Identifier: "values",
TypeAnnotation: sema.NewTypeAnnotation(
sema.NewVariableSizedType(nil, sema.AnyStructType),
),
},
},
ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.ByteArrayType),
}

func newInternalEVMTypeEncodeABIFunction(
gauge common.MemoryGauge,
) *interpreter.HostFunctionValue {
return interpreter.NewHostFunctionValue(
gauge,
internalEVMTypeEncodeABIFunctionType,
func(invocation interpreter.Invocation) interpreter.Value {
inter := invocation.Interpreter
locationRange := invocation.LocationRange

// Get `values` argument

valuesArray, ok := invocation.Arguments[0].(*interpreter.ArrayValue)
if !ok {
panic(errors.NewUnreachableError())
}

values := make([]interface{}, 0)
var arguments gethABI.Arguments

for i := 0; i < valuesArray.Count(); i++ {
element := valuesArray.Get(inter, locationRange, i)
switch value := element.(type) {
case *interpreter.StringValue:
values = append(values, value.Str)
typ, err := gethABI.NewType("string", "", nil)
if err != nil {
panic(err)
}
arguments = append(arguments, gethABI.Argument{Type: typ})
case interpreter.UInt64Value:
values = append(values, uint64(value))
typ, err := gethABI.NewType("uint64", "", nil)
if err != nil {
panic(err)
}
arguments = append(arguments, gethABI.Argument{Type: typ})
case interpreter.BoolValue:
values = append(values, bool(value))
typ, err := gethABI.NewType("bool", "", nil)
if err != nil {
panic(err)
}
arguments = append(arguments, gethABI.Argument{Type: typ})
}
}

encoded, err := arguments.Pack(values...)
if err != nil {
panic(err)
}

return interpreter.ByteSliceToByteArrayValue(inter, encoded)
},
)
}

// EVM.decodeABI

const internalEVMTypeDecodeABIFunctionName = "decodeABI"

var internalEVMTypeDecodeABIFunctionType = &sema.FunctionType{
Parameters: []sema.Parameter{
{
Label: sema.ArgumentLabelNotRequired,
Identifier: "types",
TypeAnnotation: sema.NewTypeAnnotation(
sema.NewVariableSizedType(nil, sema.AnyType),
),
},
{
Label: "data",
TypeAnnotation: sema.NewTypeAnnotation(sema.ByteArrayType),
},
},
ReturnTypeAnnotation: sema.NewTypeAnnotation(
sema.NewVariableSizedType(nil, sema.AnyStructType),
),
}

func newInternalEVMTypeDecodeABIFunction(
gauge common.MemoryGauge,
) *interpreter.HostFunctionValue {
return interpreter.NewHostFunctionValue(
gauge,
internalEVMTypeDecodeABIFunctionType,
func(invocation interpreter.Invocation) interpreter.Value {
inter := invocation.Interpreter
locationRange := invocation.LocationRange

// Get `types` argument

types, ok := invocation.Arguments[0].(*interpreter.ArrayValue)
if !ok {
panic(errors.NewUnreachableError())
}

// Get `data` argument

dataValue, ok := invocation.Arguments[1].(*interpreter.ArrayValue)
if !ok {
panic(errors.NewUnreachableError())
}

data, err := interpreter.ByteArrayValueToByteSlice(inter, dataValue, locationRange)
if err != nil {
panic(err)
}

var arguments gethABI.Arguments
for i := 0; i < types.Count(); i++ {
arg := types.Get(inter, locationRange, i)
switch arg.String() {
case "Type<String>()":
typ, err := gethABI.NewType("string", "", nil)
if err != nil {
panic(err)
}
arguments = append(arguments, gethABI.Argument{Type: typ})
case "Type<UInt64>()":
typ, err := gethABI.NewType("uint64", "", nil)
if err != nil {
panic(err)
}
arguments = append(arguments, gethABI.Argument{Type: typ})
case "Type<Bool>()":
typ, err := gethABI.NewType("bool", "", nil)
if err != nil {
panic(err)
}
arguments = append(arguments, gethABI.Argument{Type: typ})
}
}

decoded, err := arguments.Unpack(data)
if err != nil {
panic(err)
}

values := make([]interpreter.Value, 0)
for _, arg := range decoded {
switch value := arg.(type) {
case string:
values = append(values, interpreter.NewStringValue(
inter,
common.NewStringMemoryUsage(len(value)),
func() string {
return value
},
))
case uint64:
values = append(values, interpreter.NewUInt64Value(
inter,
func() uint64 {
return value
},
))
case bool:
values = append(values, interpreter.BoolValue(value))
}
}
arrayType := interpreter.NewVariableSizedStaticType(
inter,
interpreter.NewPrimitiveStaticType(
inter,
interpreter.PrimitiveStaticTypeAnyStruct,
),
)

return interpreter.NewArrayValue(
inter,
invocation.LocationRange,
arrayType,
common.ZeroAddress,
values...,
)
},
)
}

const internalEVMTypeRunFunctionName = "run"

var internalEVMTypeRunFunctionType = &sema.FunctionType{
Expand Down Expand Up @@ -538,6 +736,8 @@ func NewInternalEVMContractValue(
internalEVMTypeDepositFunctionName: newInternalEVMTypeDepositFunction(gauge, handler),
internalEVMTypeWithdrawFunctionName: newInternalEVMTypeWithdrawFunction(gauge, handler),
internalEVMTypeDeployFunctionName: newInternalEVMTypeDeployFunction(gauge, handler),
internalEVMTypeEncodeABIFunctionName: newInternalEVMTypeEncodeABIFunction(gauge),
internalEVMTypeDecodeABIFunctionName: newInternalEVMTypeDecodeABIFunction(gauge),
},
nil,
nil,
Expand Down Expand Up @@ -578,6 +778,12 @@ var InternalEVMContractType = func() *sema.CompositeType {
internalEVMTypeDepositFunctionType,
"",
),
sema.NewUnmeteredPublicFunctionMember(
ty,
internalEVMTypeEncodeABIFunctionName,
internalEVMTypeEncodeABIFunctionType,
"",
),
sema.NewUnmeteredPublicFunctionMember(
ty,
internalEVMTypeWithdrawFunctionName,
Expand All @@ -590,6 +796,12 @@ var InternalEVMContractType = func() *sema.CompositeType {
internalEVMTypeDeployFunctionType,
"",
),
sema.NewUnmeteredPublicFunctionMember(
ty,
internalEVMTypeDecodeABIFunctionName,
internalEVMTypeDecodeABIFunctionType,
"",
),
})
return ty
}()
Expand Down
Loading

0 comments on commit 0b25342

Please sign in to comment.