Skip to content

Commit adf5155

Browse files
authored
Add mappings: url.URL from/to string & time.Time from/to uint/int (#11)
1 parent 93f9e58 commit adf5155

File tree

2 files changed

+181
-8
lines changed

2 files changed

+181
-8
lines changed

abi/value.go

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import (
44
"fmt"
55
"math"
66
"math/big"
7+
"net/url"
78
"reflect"
9+
"time"
810

911
"github.com/defiweb/go-eth/hexutil"
1012
"github.com/defiweb/go-eth/types"
@@ -325,7 +327,13 @@ func (s *StringValue) MapFrom(m Mapper, src any) error {
325327
case reflect.String:
326328
*s = StringValue(srcRef.String())
327329
default:
328-
return fmt.Errorf("abi: cannot map %s to string", srcRef.Type())
330+
switch srcTyp := srcRef.Interface().(type) {
331+
case url.URL:
332+
*s = StringValue(srcTyp.String())
333+
return nil
334+
default:
335+
return fmt.Errorf("abi: cannot map %s to string", srcRef.Type())
336+
}
329337
}
330338
return nil
331339
}
@@ -346,7 +354,16 @@ func (s *StringValue) MapTo(m Mapper, dst any) error {
346354
case reflect.Interface:
347355
dstRef.Set(reflect.ValueOf(string(*s)))
348356
default:
349-
return fmt.Errorf("abi: cannot map string to %s", dstRef.Type())
357+
switch dstRef.Interface().(type) {
358+
case url.URL:
359+
u, err := url.Parse(string(*s))
360+
if err != nil {
361+
return fmt.Errorf("abi: cannot map string to %s: %v", dstRef.Type(), err)
362+
}
363+
dstRef.Set(reflect.ValueOf(*u))
364+
default:
365+
return fmt.Errorf("abi: cannot map string to %s", dstRef.Type())
366+
}
350367
}
351368
return nil
352369
}
@@ -687,6 +704,15 @@ func (u *UintValue) MapFrom(_ Mapper, src any) error {
687704
return fmt.Errorf("abi: cannot map %s to uint%d: value too large", srcRef.Type(), u.Size)
688705
}
689706
u.Int = *bn
707+
case time.Time:
708+
bn := new(big.Int).SetInt64(srcTyp.Unix())
709+
if bn.Sign() < 0 {
710+
return fmt.Errorf("abi: cannot map %s to uint%d: negative value", srcRef.Type(), u.Size)
711+
}
712+
if bn.BitLen() > u.Size {
713+
return fmt.Errorf("abi: cannot map %s to uint%d: value too large", srcRef.Type(), u.Size)
714+
}
715+
u.Int = *bn
690716
default:
691717
return fmt.Errorf("abi: cannot map %s to uint%d", srcRef.Type(), u.Size)
692718
}
@@ -714,6 +740,8 @@ func (u *UintValue) MapTo(_ Mapper, dst any) error {
714740
dstRef.Set(reflect.ValueOf(&u.Int))
715741
default:
716742
switch dstRef.Interface().(type) {
743+
case time.Time:
744+
dstRef.Set(reflect.ValueOf(time.Unix(u.Int.Int64(), 0)))
717745
case big.Int:
718746
dstRef.Set(reflect.ValueOf(u.Int))
719747
case types.Number:
@@ -809,6 +837,12 @@ func (i *IntValue) MapFrom(_ Mapper, src any) error {
809837
return fmt.Errorf("abi: cannot map %s to uint%d: value too large", srcRef.Type(), i.Size)
810838
}
811839
i.Int = *bn
840+
case time.Time:
841+
bn := new(big.Int).SetInt64(srcTyp.Unix())
842+
if signedBitLen(bn) > i.Size {
843+
return fmt.Errorf("abi: cannot map %s to uint%d: value too large", srcRef.Type(), i.Size)
844+
}
845+
i.Int = *bn
812846
default:
813847
return fmt.Errorf("abi: cannot map %s to uint%d", srcRef.Type(), i.Size)
814848
}
@@ -839,6 +873,8 @@ func (i *IntValue) MapTo(_ Mapper, dst any) error {
839873
dstRef.Set(reflect.ValueOf(&i.Int))
840874
default:
841875
switch dstRef.Interface().(type) {
876+
case time.Time:
877+
dstRef.Set(reflect.ValueOf(time.Unix(i.Int.Int64(), 0)))
842878
case big.Int:
843879
dstRef.Set(reflect.ValueOf(i.Int))
844880
case types.Number:

abi/value_test.go

Lines changed: 143 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ package abi
22

33
import (
44
"bytes"
5+
"math"
56
"math/big"
7+
"net/url"
68
"strings"
79
"testing"
10+
"time"
811

912
"github.com/stretchr/testify/assert"
1013
"github.com/stretchr/testify/require"
@@ -207,6 +210,15 @@ func TestEncodeABI(t *testing.T) {
207210
padR("61"), // data
208211
},
209212
},
213+
{
214+
name: "string#url",
215+
val: new(StringValue),
216+
arg: must(url.Parse("http://example.com")),
217+
want: Words{
218+
padL("12"), // length
219+
padR("687474703a2f2f6578616d706c652e636f6d"), // data
220+
},
221+
},
210222
// FixedBytesValue:
211223
{
212224
name: "fixed#bytes-empty",
@@ -234,10 +246,22 @@ func TestEncodeABI(t *testing.T) {
234246
want: Words{padL("00")},
235247
},
236248
{
237-
name: "uint256#MaxUint256",
238-
val: &UintValue{Size: 256},
239-
arg: new(big.Int).Set(MaxUint[256]),
240-
want: Words{padR("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")},
249+
name: "uint256#time.Unix-0",
250+
val: &UintValue{Size: 64},
251+
arg: time.Unix(0, 0),
252+
want: Words{padL("00")},
253+
},
254+
{
255+
name: "uint256#time.Unix-max-min",
256+
val: &UintValue{Size: 64},
257+
arg: time.Unix(math.MaxInt64, math.MinInt64),
258+
want: Words{padL("7ffffffdda3e82fa")},
259+
},
260+
{
261+
name: "uint256#time.Unix-min-min",
262+
val: &UintValue{Size: 64},
263+
arg: time.Unix(math.MinInt64, math.MinInt64),
264+
want: Words{padL("7ffffffdda3e82fb")},
241265
},
242266
// IntValue:
243267
{
@@ -288,6 +312,36 @@ func TestEncodeABI(t *testing.T) {
288312
arg: big.NewInt(-128),
289313
want: Words{padR("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80")},
290314
},
315+
{
316+
name: "int256#time.Unix-0",
317+
val: &IntValue{Size: 256},
318+
arg: time.Unix(0, 0),
319+
want: Words{padL("00")},
320+
},
321+
{
322+
name: "int256#time.Unix-max-min",
323+
val: &IntValue{Size: 256},
324+
arg: time.Unix(math.MaxInt64, math.MinInt64),
325+
want: Words{padL("7ffffffdda3e82fa")},
326+
},
327+
{
328+
name: "int256#time.Unix-min-min",
329+
val: &IntValue{Size: 256},
330+
arg: time.Unix(math.MinInt64, math.MinInt64),
331+
want: Words{padL("7ffffffdda3e82fb")},
332+
},
333+
{
334+
name: "int256#time.Unix-min-max",
335+
val: &IntValue{Size: 256},
336+
arg: time.Unix(math.MinInt64, math.MaxInt64),
337+
want: Words{Word{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x02, 0x25, 0xc1, 0x7d, 0x04}},
338+
},
339+
{
340+
name: "int256#time.Unix-max-max",
341+
val: &IntValue{Size: 256},
342+
arg: time.Unix(math.MaxInt64, math.MaxInt64),
343+
want: Words{Word{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x02, 0x25, 0xc1, 0x7d, 0x03}},
344+
},
291345
// BoolValue:
292346
{
293347
name: "bool#true",
@@ -329,6 +383,13 @@ func TestEncodeABI(t *testing.T) {
329383
}
330384
}
331385

386+
func must[T any](v T, err error) T {
387+
if err != nil {
388+
panic(err)
389+
}
390+
return v
391+
}
392+
332393
func TestDecodeABI(t *testing.T) {
333394
tests := []struct {
334395
name string
@@ -772,6 +833,15 @@ func TestMapFrom(t *testing.T) {
772833
padR("010203"), // data
773834
},
774835
},
836+
{
837+
name: "url.URL->string",
838+
val: new(StringValue),
839+
data: must(url.Parse("http://example.com")),
840+
want: Words{
841+
padL("12"), // length
842+
padR("687474703a2f2f6578616d706c652e636f6d"), // data
843+
},
844+
},
775845
{
776846
name: "array->string",
777847
val: new(StringValue),
@@ -1203,6 +1273,50 @@ func TestMapFrom(t *testing.T) {
12031273
padL("2a"),
12041274
},
12051275
},
1276+
{
1277+
name: "time.Time->int256",
1278+
val: &IntValue{Size: 256},
1279+
data: time.Unix(0, 0),
1280+
want: Words{padL("00")},
1281+
},
1282+
{
1283+
name: "time.Unix(math.MaxInt64, math.MinInt64)->int256",
1284+
val: &IntValue{Size: 256},
1285+
data: time.Unix(math.MaxInt64, math.MinInt64),
1286+
want: Words{padL("7ffffffdda3e82fa")},
1287+
},
1288+
{
1289+
name: "time.Unix(math.MinInt64, math.MinInt64)->int256",
1290+
val: &IntValue{Size: 256},
1291+
data: time.Unix(math.MinInt64, math.MinInt64),
1292+
want: Words{padL("7ffffffdda3e82fb")},
1293+
},
1294+
{
1295+
name: "time.Unix(math.MinInt64, math.MinInt64)->int32",
1296+
val: &IntValue{Size: 32},
1297+
data: time.Unix(math.MinInt64, math.MinInt64),
1298+
wantErr: true,
1299+
},
1300+
{
1301+
name: "time.Unix(math.MinInt64, math.MinInt64)->int64",
1302+
val: &IntValue{Size: 64},
1303+
data: time.Unix(math.MinInt64, math.MinInt64),
1304+
want: Words{padL("7ffffffdda3e82fb")},
1305+
},
1306+
{
1307+
name: "time.Unix(math.MinInt64, math.MaxInt64)->int256",
1308+
val: &IntValue{Size: 256},
1309+
data: time.Unix(math.MinInt64, math.MaxInt64),
1310+
// want: Words{padLFF("8000000025c17d04")},
1311+
want: Words{Word{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x02, 0x25, 0xc1, 0x7d, 0x04}},
1312+
},
1313+
{
1314+
name: "time.Unix(math.MaxInt64, math.MaxInt64)->int256",
1315+
val: &IntValue{Size: 256},
1316+
data: time.Unix(math.MaxInt64, math.MaxInt64),
1317+
// want: Words{padLFF("8000000025c17d03")},
1318+
want: Words{Word{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x02, 0x25, 0xc1, 0x7d, 0x03}},
1319+
},
12061320
// BoolValue:
12071321
{
12081322
name: "bool->bool",
@@ -1378,6 +1492,18 @@ func TestMapTo(t *testing.T) {
13781492
val: func() Value { s := StringValue("foo"); return &s }(),
13791493
want: func() *[]byte { b := []byte("foo"); return &b }(),
13801494
},
1495+
{
1496+
name: "string->url.URL",
1497+
arg: new(url.URL),
1498+
val: func() Value { s := StringValue("http://example.com"); return &s }(),
1499+
want: func() *url.URL { return must(url.Parse("http://example.com")) }(),
1500+
},
1501+
{
1502+
name: "string->url.URL(bad)",
1503+
arg: new(url.URL),
1504+
val: func() Value { s := StringValue("http://example.com:XXX"); return &s }(),
1505+
wantErr: true,
1506+
},
13811507
{
13821508
name: "string->array",
13831509
arg: new([3]byte),
@@ -1594,8 +1720,7 @@ func TestMapTo(t *testing.T) {
15941720
want: func() *int8 { i := int8(42); return &i }(),
15951721
},
15961722
{
1597-
name: "uint16->int8#overflow",
1598-
1723+
name: "uint16->int8#overflow",
15991724
arg: new(int8),
16001725
val: func() Value { i := &UintValue{Size: 16}; i.SetUint64(128); return i }(),
16011726
wantErr: true,
@@ -1624,6 +1749,12 @@ func TestMapTo(t *testing.T) {
16241749
val: func() Value { i := &UintValue{Size: 256}; i.SetUint64(42); return i }(),
16251750
want: types.MustNumberFromHexPtr("0x2a"),
16261751
},
1752+
{
1753+
name: "uint256->time.Time",
1754+
arg: new(time.Time),
1755+
val: func() Value { i := &UintValue{Size: 256}; i.SetInt64(math.MaxInt64); return i }(),
1756+
want: func() *time.Time { t := time.Unix(math.MaxInt64, 0); return &t }(),
1757+
},
16271758
// IntValue:
16281759
{
16291760
name: "int256->string",
@@ -1709,6 +1840,12 @@ func TestMapTo(t *testing.T) {
17091840
val: func() Value { i := &IntValue{Size: 256}; i.SetUint64(42); return i }(),
17101841
want: types.MustNumberFromHexPtr("0x2a"),
17111842
},
1843+
{
1844+
name: "int256->time.Time",
1845+
arg: new(time.Time),
1846+
val: func() Value { i := &IntValue{Size: 256}; i.SetInt64(math.MaxInt64); return i }(),
1847+
want: func() *time.Time { t := time.Unix(math.MaxInt64, 0); return &t }(),
1848+
},
17121849
// BoolValue:
17131850
{
17141851
name: "bool->bool",

0 commit comments

Comments
 (0)