-
Notifications
You must be signed in to change notification settings - Fork 143
/
address.go
115 lines (100 loc) · 3.45 KB
/
address.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// Copyright 2020 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0
package iscmagic
import (
"bytes"
_ "embed"
"errors"
"github.com/ethereum/go-ethereum/common"
iotago "github.com/iotaledger/iota.go/v3"
"github.com/iotaledger/wasp/packages/hashing"
"github.com/iotaledger/wasp/packages/kv/codec"
)
type addressKind uint8
const (
addressKindISCMagic = addressKind(iota)
addressKindERC20BaseTokens
addressKindERC20NativeTokens
addressKindERC721NFTs
addressKindERC721NFTCollection
addressKindERC20ExternalNativeTokens
addressKindInvalid
)
var (
AddressPrefix = []byte{0x10, 0x74}
Address = packMagicAddress(addressKindISCMagic, nil)
kindByteIndex = len(AddressPrefix)
headerLength = len(AddressPrefix) + 1 // AddressPrefix + kind (byte)
maxPayloadLength = common.AddressLength - headerLength
)
// ERC20NativeTokensAddress returns the Ethereum address of the ERC20 contract for
// native tokens with an on-chain foundry.
func ERC20NativeTokensAddress(foundrySN uint32) common.Address {
return packMagicAddress(addressKindERC20NativeTokens, codec.EncodeUint32(foundrySN))
}
// ERC20ExternalNativeTokensAddress creates an Ethereum address for an ERC20 contract for
// native tokens with an off-chain foundry.
// NOTE: since len(NativeTokenID) == 38 and len(Address) == 20, it is not possible to assign
// a unique address to every native token. This function tries to form the ERC20 address
// from the first 17 bytes of hash(nativeTokenID). In case of a collision, it reapplies the
// hash and checks for a collision again, repeating until it gives up.
func ERC20ExternalNativeTokensAddress(
nativeTokenID iotago.NativeTokenID,
isTaken func(common.Address) bool,
) (common.Address, error) {
const maxAttempts = 10
hash := hashing.HashData(nativeTokenID[:])
for i := 0; i < maxAttempts; i++ {
addr := packMagicAddress(addressKindERC20ExternalNativeTokens, hash[:maxPayloadLength])
if !isTaken(addr) {
return addr, nil
}
hash = hashing.HashData(hash[:])
}
return common.Address{}, errors.New("all suitable addresses are taken")
}
func ERC20NativeTokensFoundrySN(addr common.Address) (uint32, error) {
kind, payload, err := unpackMagicAddress(addr)
if err != nil {
return 0, err
}
if kind != addressKindERC20NativeTokens {
return 0, errors.New("ERC20NativeTokensFoundrySN: invalid address kind")
}
if !allZero(payload[4:]) {
return 0, errors.New("ERC20NativeTokensFoundrySN: invalid address format")
}
return codec.MustDecodeUint32(payload[0:4]), nil
}
func ERC721NFTCollectionAddress(collectionID iotago.NFTID) common.Address {
return packMagicAddress(addressKindERC721NFTCollection, collectionID[:maxPayloadLength])
}
func packMagicAddress(kind addressKind, payload []byte) common.Address {
var ret common.Address
copy(ret[:], AddressPrefix)
ret[kindByteIndex] = byte(kind)
if len(payload) > maxPayloadLength {
panic("packMagicAddress: invalid payload length")
}
copy(ret[headerLength:], payload)
return ret
}
func unpackMagicAddress(addr common.Address) (addressKind, []byte, error) {
if !bytes.Equal(addr[0:len(AddressPrefix)], AddressPrefix) {
return 0, nil, errors.New("unpackMagicAddress: expected magic address prefix")
}
kind := addressKind(addr[kindByteIndex])
if kind >= addressKindInvalid {
return 0, nil, errors.New("unpackMagicAddress: unknown address kind")
}
payload := addr[headerLength:]
return kind, payload, nil
}
func allZero(s []byte) bool {
for _, v := range s {
if v != 0 {
return false
}
}
return true
}