all: consider decimals on wrapped assets, fix VAA posting, fix solana account parsing
This commit is contained in:
parent
8e6dc495dc
commit
f6750a3762
|
@ -48,53 +48,53 @@ func p2p(obsvC chan *gossipv1.LockupObservation, sendC chan []byte) func(ctx con
|
||||||
|
|
||||||
var idht *dht.IpfsDHT
|
var idht *dht.IpfsDHT
|
||||||
|
|
||||||
h, err := libp2p.New(ctx,
|
h, err := libp2p.New(ctx,
|
||||||
// Use the keypair we generated
|
// Use the keypair we generated
|
||||||
libp2p.Identity(priv),
|
libp2p.Identity(priv),
|
||||||
|
|
||||||
// Multiple listen addresses
|
// Multiple listen addresses
|
||||||
libp2p.ListenAddrStrings(
|
libp2p.ListenAddrStrings(
|
||||||
// Listen on QUIC only.
|
// Listen on QUIC only.
|
||||||
// TODO(leo): is this more or less stable than using both TCP and QUIC transports?
|
// TODO(leo): is this more or less stable than using both TCP and QUIC transports?
|
||||||
// https://github.com/libp2p/go-libp2p/issues/688
|
// https://github.com/libp2p/go-libp2p/issues/688
|
||||||
fmt.Sprintf("/ip4/0.0.0.0/udp/%d/quic", *p2pPort),
|
fmt.Sprintf("/ip4/0.0.0.0/udp/%d/quic", *p2pPort),
|
||||||
fmt.Sprintf("/ip6/::/udp/%d/quic", *p2pPort),
|
fmt.Sprintf("/ip6/::/udp/%d/quic", *p2pPort),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Enable TLS security as the only security protocol.
|
// Enable TLS security as the only security protocol.
|
||||||
libp2p.Security(libp2ptls.ID, libp2ptls.New),
|
libp2p.Security(libp2ptls.ID, libp2ptls.New),
|
||||||
|
|
||||||
// Enable QUIC transport as the only transport.
|
// Enable QUIC transport as the only transport.
|
||||||
libp2p.Transport(libp2pquic.NewTransport),
|
libp2p.Transport(libp2pquic.NewTransport),
|
||||||
|
|
||||||
// Let's prevent our peer from having too many
|
// Let's prevent our peer from having too many
|
||||||
// connections by attaching a connection manager.
|
// connections by attaching a connection manager.
|
||||||
libp2p.ConnectionManager(connmgr.NewConnManager(
|
libp2p.ConnectionManager(connmgr.NewConnManager(
|
||||||
100, // Lowwater
|
100, // Lowwater
|
||||||
400, // HighWater,
|
400, // HighWater,
|
||||||
time.Minute, // GracePeriod
|
time.Minute, // GracePeriod
|
||||||
)),
|
)),
|
||||||
|
|
||||||
// Let this host use the DHT to find other hosts
|
// Let this host use the DHT to find other hosts
|
||||||
libp2p.Routing(func(h host.Host) (routing.PeerRouting, error) {
|
libp2p.Routing(func(h host.Host) (routing.PeerRouting, error) {
|
||||||
// TODO(leo): Persistent data store (i.e. address book)
|
// TODO(leo): Persistent data store (i.e. address book)
|
||||||
idht, err = dht.New(ctx, h, dht.Mode(dht.ModeServer),
|
idht, err = dht.New(ctx, h, dht.Mode(dht.ModeServer),
|
||||||
// TODO(leo): This intentionally makes us incompatible with the global IPFS DHT
|
// TODO(leo): This intentionally makes us incompatible with the global IPFS DHT
|
||||||
dht.ProtocolPrefix(protocol.ID("/"+*p2pNetworkID)),
|
dht.ProtocolPrefix(protocol.ID("/"+*p2pNetworkID)),
|
||||||
|
)
|
||||||
|
return idht, err
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
return idht, err
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
// TODO: libp2p cannot be cleanly restarted (https://github.com/libp2p/go-libp2p/issues/992)
|
// TODO: libp2p cannot be cleanly restarted (https://github.com/libp2p/go-libp2p/issues/992)
|
||||||
logger.Error("p2p routine has exited, cancelling root context...", zap.Error(re))
|
logger.Error("p2p routine has exited, cancelling root context...", zap.Error(re))
|
||||||
rootCtxCancel()
|
rootCtxCancel()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
logger.Info("Connecting to bootstrap peers", zap.String("bootstrap_peers", *p2pBootstrap))
|
logger.Info("Connecting to bootstrap peers", zap.String("bootstrap_peers", *p2pBootstrap))
|
||||||
|
|
||||||
|
|
|
@ -130,8 +130,9 @@ func vaaConsensusProcessor(lockC chan *common.ChainLock, setC chan *common.Guard
|
||||||
SourceAddress: k.SourceAddress,
|
SourceAddress: k.SourceAddress,
|
||||||
TargetAddress: k.TargetAddress,
|
TargetAddress: k.TargetAddress,
|
||||||
Asset: &vaa.AssetMeta{
|
Asset: &vaa.AssetMeta{
|
||||||
Chain: k.TokenChain,
|
Chain: k.TokenChain,
|
||||||
Address: k.TokenAddress,
|
Address: k.TokenAddress,
|
||||||
|
Decimals: k.TokenDecimals,
|
||||||
},
|
},
|
||||||
Amount: k.Amount,
|
Amount: k.Amount,
|
||||||
},
|
},
|
||||||
|
@ -281,10 +282,15 @@ func vaaConsensusProcessor(lockC chan *common.ChainLock, setC chan *common.Guard
|
||||||
zap.Any("vaa", signed),
|
zap.Any("vaa", signed),
|
||||||
zap.String("bytes", hex.EncodeToString(vaaBytes)))
|
zap.String("bytes", hex.EncodeToString(vaaBytes)))
|
||||||
|
|
||||||
if idx == 0 {
|
if idx == 1 {
|
||||||
vaaC <- signed
|
vaaC <- signed
|
||||||
}
|
}
|
||||||
case t.TargetChain == vaa.ChainIDEthereum:
|
case t.TargetChain == vaa.ChainIDEthereum:
|
||||||
|
// cross-submit to Solana for data availability
|
||||||
|
if idx == 1 {
|
||||||
|
vaaC <- signed
|
||||||
|
}
|
||||||
|
|
||||||
timeout, cancel := context.WithTimeout(ctx, 15*time.Second)
|
timeout, cancel := context.WithTimeout(ctx, 15*time.Second)
|
||||||
tx, err := devnet.SubmitVAA(timeout, *ethRPC, signed)
|
tx, err := devnet.SubmitVAA(timeout, *ethRPC, signed)
|
||||||
cancel()
|
cancel()
|
||||||
|
@ -294,10 +300,6 @@ func vaaConsensusProcessor(lockC chan *common.ChainLock, setC chan *common.Guard
|
||||||
}
|
}
|
||||||
logger.Info("lockup submitted to Ethereum", zap.Any("tx", tx))
|
logger.Info("lockup submitted to Ethereum", zap.Any("tx", tx))
|
||||||
|
|
||||||
// cross-submit to Solana for data availability
|
|
||||||
if idx == 0 {
|
|
||||||
vaaC <- signed
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
logger.Error("we don't know how to submit this VAA",
|
logger.Error("we don't know how to submit this VAA",
|
||||||
zap.String("digest", hash),
|
zap.String("digest", hash),
|
||||||
|
|
|
@ -20,6 +20,8 @@ type signerInfo struct {
|
||||||
index int
|
index int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
keys := generateKeys(6)
|
keys := generateKeys(6)
|
||||||
|
@ -38,8 +40,9 @@ func main() {
|
||||||
SourceAddress: vaa.Address{2, 1, 4},
|
SourceAddress: vaa.Address{2, 1, 4},
|
||||||
TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress),
|
TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress),
|
||||||
Asset: &vaa.AssetMeta{
|
Asset: &vaa.AssetMeta{
|
||||||
Chain: vaa.ChainIDSolana,
|
Chain: vaa.ChainIDSolana,
|
||||||
Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"),
|
Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"),
|
||||||
|
Decimals: 8,
|
||||||
},
|
},
|
||||||
Amount: big.NewInt(1000000000000000000),
|
Amount: big.NewInt(1000000000000000000),
|
||||||
},
|
},
|
||||||
|
@ -56,8 +59,9 @@ func main() {
|
||||||
SourceAddress: vaa.Address{2, 1, 4},
|
SourceAddress: vaa.Address{2, 1, 4},
|
||||||
TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress),
|
TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress),
|
||||||
Asset: &vaa.AssetMeta{
|
Asset: &vaa.AssetMeta{
|
||||||
Chain: vaa.ChainIDEthereum,
|
Chain: vaa.ChainIDEthereum,
|
||||||
Address: hexToAddress("0xd833215cbcc3f914bd1c9ece3ee7bf8b14f841bb"),
|
Address: hexToAddress("0xd833215cbcc3f914bd1c9ece3ee7bf8b14f841bb"),
|
||||||
|
Decimals: 8,
|
||||||
},
|
},
|
||||||
Amount: big.NewInt(1000000000000000000),
|
Amount: big.NewInt(1000000000000000000),
|
||||||
},
|
},
|
||||||
|
@ -98,8 +102,9 @@ func main() {
|
||||||
SourceAddress: vaa.Address{2, 1, 4},
|
SourceAddress: vaa.Address{2, 1, 4},
|
||||||
TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress),
|
TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress),
|
||||||
Asset: &vaa.AssetMeta{
|
Asset: &vaa.AssetMeta{
|
||||||
Chain: vaa.ChainIDSolana,
|
Chain: vaa.ChainIDSolana,
|
||||||
Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"),
|
Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"),
|
||||||
|
Decimals: 8,
|
||||||
},
|
},
|
||||||
Amount: big.NewInt(1000000000000000000),
|
Amount: big.NewInt(1000000000000000000),
|
||||||
},
|
},
|
||||||
|
@ -116,8 +121,9 @@ func main() {
|
||||||
SourceAddress: vaa.Address{2, 1, 5},
|
SourceAddress: vaa.Address{2, 1, 5},
|
||||||
TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress),
|
TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress),
|
||||||
Asset: &vaa.AssetMeta{
|
Asset: &vaa.AssetMeta{
|
||||||
Chain: vaa.ChainIDSolana,
|
Chain: vaa.ChainIDSolana,
|
||||||
Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"),
|
Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"),
|
||||||
|
Decimals: 8,
|
||||||
},
|
},
|
||||||
Amount: big.NewInt(1000000000000000000),
|
Amount: big.NewInt(1000000000000000000),
|
||||||
},
|
},
|
||||||
|
@ -134,13 +140,33 @@ func main() {
|
||||||
SourceAddress: vaa.Address{2, 1, 5},
|
SourceAddress: vaa.Address{2, 1, 5},
|
||||||
TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress),
|
TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress),
|
||||||
Asset: &vaa.AssetMeta{
|
Asset: &vaa.AssetMeta{
|
||||||
Chain: vaa.ChainIDSolana,
|
Chain: vaa.ChainIDSolana,
|
||||||
Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"),
|
Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"),
|
||||||
|
Decimals: 8,
|
||||||
},
|
},
|
||||||
Amount: big.NewInt(1000000000000000000),
|
Amount: big.NewInt(1000000000000000000),
|
||||||
},
|
},
|
||||||
}, []*signerInfo{{keys[1], 0}})
|
}, []*signerInfo{{keys[1], 0}})
|
||||||
|
|
||||||
|
signAndPrintVAA(&vaa.VAA{
|
||||||
|
Version: 1,
|
||||||
|
GuardianSetIndex: 1,
|
||||||
|
Timestamp: time.Unix(2000, 0),
|
||||||
|
Payload: &vaa.BodyTransfer{
|
||||||
|
Nonce: 57,
|
||||||
|
SourceChain: 1,
|
||||||
|
TargetChain: 2,
|
||||||
|
SourceAddress: vaa.Address{2, 1, 5},
|
||||||
|
TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress),
|
||||||
|
Asset: &vaa.AssetMeta{
|
||||||
|
Chain: vaa.ChainIDSolana,
|
||||||
|
Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"),
|
||||||
|
Decimals: 8,
|
||||||
|
},
|
||||||
|
Amount: big.NewInt(1000000000000000000),
|
||||||
|
},
|
||||||
|
}, []*signerInfo{{keys[0], 0}})
|
||||||
|
|
||||||
signAndPrintVAA(&vaa.VAA{
|
signAndPrintVAA(&vaa.VAA{
|
||||||
Version: 1,
|
Version: 1,
|
||||||
GuardianSetIndex: 1,
|
GuardianSetIndex: 1,
|
||||||
|
@ -169,8 +195,9 @@ func main() {
|
||||||
SourceAddress: vaa.Address{2, 1, 5},
|
SourceAddress: vaa.Address{2, 1, 5},
|
||||||
TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress),
|
TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress),
|
||||||
Asset: &vaa.AssetMeta{
|
Asset: &vaa.AssetMeta{
|
||||||
Chain: vaa.ChainIDSolana,
|
Chain: vaa.ChainIDSolana,
|
||||||
Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"),
|
Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"),
|
||||||
|
Decimals: 8,
|
||||||
},
|
},
|
||||||
Amount: big.NewInt(1000000000000000000),
|
Amount: big.NewInt(1000000000000000000),
|
||||||
},
|
},
|
||||||
|
@ -187,8 +214,9 @@ func main() {
|
||||||
SourceAddress: vaa.Address{2, 1, 5},
|
SourceAddress: vaa.Address{2, 1, 5},
|
||||||
TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress),
|
TargetAddress: padAddress(devnet.GanacheClientDefaultAccountAddress),
|
||||||
Asset: &vaa.AssetMeta{
|
Asset: &vaa.AssetMeta{
|
||||||
Chain: vaa.ChainIDSolana,
|
Chain: vaa.ChainIDSolana,
|
||||||
Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"),
|
Address: hexToAddress("0x347ef34687bdc9f189e87a9200658d9c40e9988"),
|
||||||
|
Decimals: 8,
|
||||||
},
|
},
|
||||||
Amount: big.NewInt(1000000000000000000),
|
Amount: big.NewInt(1000000000000000000),
|
||||||
},
|
},
|
||||||
|
@ -203,7 +231,8 @@ func signAndPrintVAA(vaa *vaa.VAA, signers []*signerInfo) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
println(hex.EncodeToString(vData))
|
println(i, hex.EncodeToString(vData))
|
||||||
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateKeys(n int) (keys []*ecdsa.PrivateKey) {
|
func generateKeys(n int) (keys []*ecdsa.PrivateKey) {
|
||||||
|
|
|
@ -21,8 +21,9 @@ type ChainLock struct {
|
||||||
SourceChain vaa.ChainID
|
SourceChain vaa.ChainID
|
||||||
TargetChain vaa.ChainID
|
TargetChain vaa.ChainID
|
||||||
|
|
||||||
TokenChain vaa.ChainID
|
TokenChain vaa.ChainID
|
||||||
TokenAddress vaa.Address
|
TokenAddress vaa.Address
|
||||||
|
TokenDecimals uint8
|
||||||
|
|
||||||
Amount *big.Int
|
Amount *big.Int
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ var (
|
||||||
_ = big.NewInt
|
_ = big.NewInt
|
||||||
_ = strings.NewReader
|
_ = strings.NewReader
|
||||||
_ = ethereum.NotFound
|
_ = ethereum.NotFound
|
||||||
_ = abi.U256
|
|
||||||
_ = bind.Bind
|
_ = bind.Bind
|
||||||
_ = common.Big1
|
_ = common.Big1
|
||||||
_ = types.BloomLookup
|
_ = types.BloomLookup
|
||||||
|
@ -34,7 +33,7 @@ type WormholeGuardianSet struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AbiABI is the input ABI used to generate the binding from.
|
// AbiABI is the input ABI used to generate the binding from.
|
||||||
const AbiABI = "[{\"inputs\":[{\"components\":[{\"internalType\":\"address[]\",\"name\":\"keys\",\"type\":\"address[]\"},{\"internalType\":\"uint32\",\"name\":\"expiration_time\",\"type\":\"uint32\"}],\"internalType\":\"structWormhole.GuardianSet\",\"name\":\"initial_guardian_set\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"wrapped_asset_master\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"_guardian_set_expirity\",\"type\":\"uint32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"oldGuardianIndex\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"newGuardianIndex\",\"type\":\"uint32\"}],\"name\":\"LogGuardianSetChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"target_chain\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"token_chain\",\"type\":\"uint8\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"token\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"sender\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"}],\"name\":\"LogTokensLocked\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"guardian_set_expirity\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"guardian_set_index\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"name\":\"guardian_sets\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"expiration_time\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isWrappedAsset\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"wrappedAssetMaster\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"wrappedAssets\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"idx\",\"type\":\"uint32\"}],\"name\":\"getGuardianSet\",\"outputs\":[{\"components\":[{\"internalType\":\"address[]\",\"name\":\"keys\",\"type\":\"address[]\"},{\"internalType\":\"uint32\",\"name\":\"expiration_time\",\"type\":\"uint32\"}],\"internalType\":\"structWormhole.GuardianSet\",\"name\":\"gs\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"vaa\",\"type\":\"bytes\"}],\"name\":\"submitVAA\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"target_chain\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"}],\"name\":\"lockAssets\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"target_chain\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"}],\"name\":\"lockETH\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"}]"
|
const AbiABI = "[{\"inputs\":[{\"components\":[{\"internalType\":\"address[]\",\"name\":\"keys\",\"type\":\"address[]\"},{\"internalType\":\"uint32\",\"name\":\"expiration_time\",\"type\":\"uint32\"}],\"internalType\":\"structWormhole.GuardianSet\",\"name\":\"initial_guardian_set\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"wrapped_asset_master\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"_guardian_set_expirity\",\"type\":\"uint32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"oldGuardianIndex\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"newGuardianIndex\",\"type\":\"uint32\"}],\"name\":\"LogGuardianSetChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"target_chain\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"token_chain\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"token_decimals\",\"type\":\"uint8\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"token\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"sender\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"}],\"name\":\"LogTokensLocked\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"consumedVAAs\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"guardian_set_expirity\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"guardian_set_index\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"name\":\"guardian_sets\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"expiration_time\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isWrappedAsset\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"wrappedAssetMaster\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"wrappedAssets\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"idx\",\"type\":\"uint32\"}],\"name\":\"getGuardianSet\",\"outputs\":[{\"components\":[{\"internalType\":\"address[]\",\"name\":\"keys\",\"type\":\"address[]\"},{\"internalType\":\"uint32\",\"name\":\"expiration_time\",\"type\":\"uint32\"}],\"internalType\":\"structWormhole.GuardianSet\",\"name\":\"gs\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"vaa\",\"type\":\"bytes\"}],\"name\":\"submitVAA\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"target_chain\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"}],\"name\":\"lockAssets\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"target_chain\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"}],\"name\":\"lockETH\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"}]"
|
||||||
|
|
||||||
// Abi is an auto generated Go binding around an Ethereum contract.
|
// Abi is an auto generated Go binding around an Ethereum contract.
|
||||||
type Abi struct {
|
type Abi struct {
|
||||||
|
@ -178,9 +177,35 @@ func (_Abi *AbiTransactorRaw) Transact(opts *bind.TransactOpts, method string, p
|
||||||
return _Abi.Contract.contract.Transact(opts, method, params...)
|
return _Abi.Contract.contract.Transact(opts, method, params...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConsumedVAAs is a free data retrieval call binding the contract method 0xa31fe409.
|
||||||
|
//
|
||||||
|
// Solidity: function consumedVAAs(bytes32 ) view returns(bool)
|
||||||
|
func (_Abi *AbiCaller) ConsumedVAAs(opts *bind.CallOpts, arg0 [32]byte) (bool, error) {
|
||||||
|
var (
|
||||||
|
ret0 = new(bool)
|
||||||
|
)
|
||||||
|
out := ret0
|
||||||
|
err := _Abi.contract.Call(opts, out, "consumedVAAs", arg0)
|
||||||
|
return *ret0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConsumedVAAs is a free data retrieval call binding the contract method 0xa31fe409.
|
||||||
|
//
|
||||||
|
// Solidity: function consumedVAAs(bytes32 ) view returns(bool)
|
||||||
|
func (_Abi *AbiSession) ConsumedVAAs(arg0 [32]byte) (bool, error) {
|
||||||
|
return _Abi.Contract.ConsumedVAAs(&_Abi.CallOpts, arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConsumedVAAs is a free data retrieval call binding the contract method 0xa31fe409.
|
||||||
|
//
|
||||||
|
// Solidity: function consumedVAAs(bytes32 ) view returns(bool)
|
||||||
|
func (_Abi *AbiCallerSession) ConsumedVAAs(arg0 [32]byte) (bool, error) {
|
||||||
|
return _Abi.Contract.ConsumedVAAs(&_Abi.CallOpts, arg0)
|
||||||
|
}
|
||||||
|
|
||||||
// GetGuardianSet is a free data retrieval call binding the contract method 0xf951975a.
|
// GetGuardianSet is a free data retrieval call binding the contract method 0xf951975a.
|
||||||
//
|
//
|
||||||
// Solidity: function getGuardianSet(uint32 idx) constant returns(WormholeGuardianSet gs)
|
// Solidity: function getGuardianSet(uint32 idx) view returns((address[],uint32) gs)
|
||||||
func (_Abi *AbiCaller) GetGuardianSet(opts *bind.CallOpts, idx uint32) (WormholeGuardianSet, error) {
|
func (_Abi *AbiCaller) GetGuardianSet(opts *bind.CallOpts, idx uint32) (WormholeGuardianSet, error) {
|
||||||
var (
|
var (
|
||||||
ret0 = new(WormholeGuardianSet)
|
ret0 = new(WormholeGuardianSet)
|
||||||
|
@ -192,21 +217,21 @@ func (_Abi *AbiCaller) GetGuardianSet(opts *bind.CallOpts, idx uint32) (Wormhole
|
||||||
|
|
||||||
// GetGuardianSet is a free data retrieval call binding the contract method 0xf951975a.
|
// GetGuardianSet is a free data retrieval call binding the contract method 0xf951975a.
|
||||||
//
|
//
|
||||||
// Solidity: function getGuardianSet(uint32 idx) constant returns(WormholeGuardianSet gs)
|
// Solidity: function getGuardianSet(uint32 idx) view returns((address[],uint32) gs)
|
||||||
func (_Abi *AbiSession) GetGuardianSet(idx uint32) (WormholeGuardianSet, error) {
|
func (_Abi *AbiSession) GetGuardianSet(idx uint32) (WormholeGuardianSet, error) {
|
||||||
return _Abi.Contract.GetGuardianSet(&_Abi.CallOpts, idx)
|
return _Abi.Contract.GetGuardianSet(&_Abi.CallOpts, idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGuardianSet is a free data retrieval call binding the contract method 0xf951975a.
|
// GetGuardianSet is a free data retrieval call binding the contract method 0xf951975a.
|
||||||
//
|
//
|
||||||
// Solidity: function getGuardianSet(uint32 idx) constant returns(WormholeGuardianSet gs)
|
// Solidity: function getGuardianSet(uint32 idx) view returns((address[],uint32) gs)
|
||||||
func (_Abi *AbiCallerSession) GetGuardianSet(idx uint32) (WormholeGuardianSet, error) {
|
func (_Abi *AbiCallerSession) GetGuardianSet(idx uint32) (WormholeGuardianSet, error) {
|
||||||
return _Abi.Contract.GetGuardianSet(&_Abi.CallOpts, idx)
|
return _Abi.Contract.GetGuardianSet(&_Abi.CallOpts, idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GuardianSetExpirity is a free data retrieval call binding the contract method 0x4db47840.
|
// GuardianSetExpirity is a free data retrieval call binding the contract method 0x4db47840.
|
||||||
//
|
//
|
||||||
// Solidity: function guardian_set_expirity() constant returns(uint32)
|
// Solidity: function guardian_set_expirity() view returns(uint32)
|
||||||
func (_Abi *AbiCaller) GuardianSetExpirity(opts *bind.CallOpts) (uint32, error) {
|
func (_Abi *AbiCaller) GuardianSetExpirity(opts *bind.CallOpts) (uint32, error) {
|
||||||
var (
|
var (
|
||||||
ret0 = new(uint32)
|
ret0 = new(uint32)
|
||||||
|
@ -218,21 +243,21 @@ func (_Abi *AbiCaller) GuardianSetExpirity(opts *bind.CallOpts) (uint32, error)
|
||||||
|
|
||||||
// GuardianSetExpirity is a free data retrieval call binding the contract method 0x4db47840.
|
// GuardianSetExpirity is a free data retrieval call binding the contract method 0x4db47840.
|
||||||
//
|
//
|
||||||
// Solidity: function guardian_set_expirity() constant returns(uint32)
|
// Solidity: function guardian_set_expirity() view returns(uint32)
|
||||||
func (_Abi *AbiSession) GuardianSetExpirity() (uint32, error) {
|
func (_Abi *AbiSession) GuardianSetExpirity() (uint32, error) {
|
||||||
return _Abi.Contract.GuardianSetExpirity(&_Abi.CallOpts)
|
return _Abi.Contract.GuardianSetExpirity(&_Abi.CallOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GuardianSetExpirity is a free data retrieval call binding the contract method 0x4db47840.
|
// GuardianSetExpirity is a free data retrieval call binding the contract method 0x4db47840.
|
||||||
//
|
//
|
||||||
// Solidity: function guardian_set_expirity() constant returns(uint32)
|
// Solidity: function guardian_set_expirity() view returns(uint32)
|
||||||
func (_Abi *AbiCallerSession) GuardianSetExpirity() (uint32, error) {
|
func (_Abi *AbiCallerSession) GuardianSetExpirity() (uint32, error) {
|
||||||
return _Abi.Contract.GuardianSetExpirity(&_Abi.CallOpts)
|
return _Abi.Contract.GuardianSetExpirity(&_Abi.CallOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GuardianSetIndex is a free data retrieval call binding the contract method 0x822d82b3.
|
// GuardianSetIndex is a free data retrieval call binding the contract method 0x822d82b3.
|
||||||
//
|
//
|
||||||
// Solidity: function guardian_set_index() constant returns(uint32)
|
// Solidity: function guardian_set_index() view returns(uint32)
|
||||||
func (_Abi *AbiCaller) GuardianSetIndex(opts *bind.CallOpts) (uint32, error) {
|
func (_Abi *AbiCaller) GuardianSetIndex(opts *bind.CallOpts) (uint32, error) {
|
||||||
var (
|
var (
|
||||||
ret0 = new(uint32)
|
ret0 = new(uint32)
|
||||||
|
@ -244,21 +269,21 @@ func (_Abi *AbiCaller) GuardianSetIndex(opts *bind.CallOpts) (uint32, error) {
|
||||||
|
|
||||||
// GuardianSetIndex is a free data retrieval call binding the contract method 0x822d82b3.
|
// GuardianSetIndex is a free data retrieval call binding the contract method 0x822d82b3.
|
||||||
//
|
//
|
||||||
// Solidity: function guardian_set_index() constant returns(uint32)
|
// Solidity: function guardian_set_index() view returns(uint32)
|
||||||
func (_Abi *AbiSession) GuardianSetIndex() (uint32, error) {
|
func (_Abi *AbiSession) GuardianSetIndex() (uint32, error) {
|
||||||
return _Abi.Contract.GuardianSetIndex(&_Abi.CallOpts)
|
return _Abi.Contract.GuardianSetIndex(&_Abi.CallOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GuardianSetIndex is a free data retrieval call binding the contract method 0x822d82b3.
|
// GuardianSetIndex is a free data retrieval call binding the contract method 0x822d82b3.
|
||||||
//
|
//
|
||||||
// Solidity: function guardian_set_index() constant returns(uint32)
|
// Solidity: function guardian_set_index() view returns(uint32)
|
||||||
func (_Abi *AbiCallerSession) GuardianSetIndex() (uint32, error) {
|
func (_Abi *AbiCallerSession) GuardianSetIndex() (uint32, error) {
|
||||||
return _Abi.Contract.GuardianSetIndex(&_Abi.CallOpts)
|
return _Abi.Contract.GuardianSetIndex(&_Abi.CallOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GuardianSets is a free data retrieval call binding the contract method 0x42b0aefa.
|
// GuardianSets is a free data retrieval call binding the contract method 0x42b0aefa.
|
||||||
//
|
//
|
||||||
// Solidity: function guardian_sets(uint32 ) constant returns(uint32 expiration_time)
|
// Solidity: function guardian_sets(uint32 ) view returns(uint32 expiration_time)
|
||||||
func (_Abi *AbiCaller) GuardianSets(opts *bind.CallOpts, arg0 uint32) (uint32, error) {
|
func (_Abi *AbiCaller) GuardianSets(opts *bind.CallOpts, arg0 uint32) (uint32, error) {
|
||||||
var (
|
var (
|
||||||
ret0 = new(uint32)
|
ret0 = new(uint32)
|
||||||
|
@ -270,21 +295,21 @@ func (_Abi *AbiCaller) GuardianSets(opts *bind.CallOpts, arg0 uint32) (uint32, e
|
||||||
|
|
||||||
// GuardianSets is a free data retrieval call binding the contract method 0x42b0aefa.
|
// GuardianSets is a free data retrieval call binding the contract method 0x42b0aefa.
|
||||||
//
|
//
|
||||||
// Solidity: function guardian_sets(uint32 ) constant returns(uint32 expiration_time)
|
// Solidity: function guardian_sets(uint32 ) view returns(uint32 expiration_time)
|
||||||
func (_Abi *AbiSession) GuardianSets(arg0 uint32) (uint32, error) {
|
func (_Abi *AbiSession) GuardianSets(arg0 uint32) (uint32, error) {
|
||||||
return _Abi.Contract.GuardianSets(&_Abi.CallOpts, arg0)
|
return _Abi.Contract.GuardianSets(&_Abi.CallOpts, arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GuardianSets is a free data retrieval call binding the contract method 0x42b0aefa.
|
// GuardianSets is a free data retrieval call binding the contract method 0x42b0aefa.
|
||||||
//
|
//
|
||||||
// Solidity: function guardian_sets(uint32 ) constant returns(uint32 expiration_time)
|
// Solidity: function guardian_sets(uint32 ) view returns(uint32 expiration_time)
|
||||||
func (_Abi *AbiCallerSession) GuardianSets(arg0 uint32) (uint32, error) {
|
func (_Abi *AbiCallerSession) GuardianSets(arg0 uint32) (uint32, error) {
|
||||||
return _Abi.Contract.GuardianSets(&_Abi.CallOpts, arg0)
|
return _Abi.Contract.GuardianSets(&_Abi.CallOpts, arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsWrappedAsset is a free data retrieval call binding the contract method 0x1a2be4da.
|
// IsWrappedAsset is a free data retrieval call binding the contract method 0x1a2be4da.
|
||||||
//
|
//
|
||||||
// Solidity: function isWrappedAsset(address ) constant returns(bool)
|
// Solidity: function isWrappedAsset(address ) view returns(bool)
|
||||||
func (_Abi *AbiCaller) IsWrappedAsset(opts *bind.CallOpts, arg0 common.Address) (bool, error) {
|
func (_Abi *AbiCaller) IsWrappedAsset(opts *bind.CallOpts, arg0 common.Address) (bool, error) {
|
||||||
var (
|
var (
|
||||||
ret0 = new(bool)
|
ret0 = new(bool)
|
||||||
|
@ -296,21 +321,21 @@ func (_Abi *AbiCaller) IsWrappedAsset(opts *bind.CallOpts, arg0 common.Address)
|
||||||
|
|
||||||
// IsWrappedAsset is a free data retrieval call binding the contract method 0x1a2be4da.
|
// IsWrappedAsset is a free data retrieval call binding the contract method 0x1a2be4da.
|
||||||
//
|
//
|
||||||
// Solidity: function isWrappedAsset(address ) constant returns(bool)
|
// Solidity: function isWrappedAsset(address ) view returns(bool)
|
||||||
func (_Abi *AbiSession) IsWrappedAsset(arg0 common.Address) (bool, error) {
|
func (_Abi *AbiSession) IsWrappedAsset(arg0 common.Address) (bool, error) {
|
||||||
return _Abi.Contract.IsWrappedAsset(&_Abi.CallOpts, arg0)
|
return _Abi.Contract.IsWrappedAsset(&_Abi.CallOpts, arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsWrappedAsset is a free data retrieval call binding the contract method 0x1a2be4da.
|
// IsWrappedAsset is a free data retrieval call binding the contract method 0x1a2be4da.
|
||||||
//
|
//
|
||||||
// Solidity: function isWrappedAsset(address ) constant returns(bool)
|
// Solidity: function isWrappedAsset(address ) view returns(bool)
|
||||||
func (_Abi *AbiCallerSession) IsWrappedAsset(arg0 common.Address) (bool, error) {
|
func (_Abi *AbiCallerSession) IsWrappedAsset(arg0 common.Address) (bool, error) {
|
||||||
return _Abi.Contract.IsWrappedAsset(&_Abi.CallOpts, arg0)
|
return _Abi.Contract.IsWrappedAsset(&_Abi.CallOpts, arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WrappedAssetMaster is a free data retrieval call binding the contract method 0x99da1d3c.
|
// WrappedAssetMaster is a free data retrieval call binding the contract method 0x99da1d3c.
|
||||||
//
|
//
|
||||||
// Solidity: function wrappedAssetMaster() constant returns(address)
|
// Solidity: function wrappedAssetMaster() view returns(address)
|
||||||
func (_Abi *AbiCaller) WrappedAssetMaster(opts *bind.CallOpts) (common.Address, error) {
|
func (_Abi *AbiCaller) WrappedAssetMaster(opts *bind.CallOpts) (common.Address, error) {
|
||||||
var (
|
var (
|
||||||
ret0 = new(common.Address)
|
ret0 = new(common.Address)
|
||||||
|
@ -322,21 +347,21 @@ func (_Abi *AbiCaller) WrappedAssetMaster(opts *bind.CallOpts) (common.Address,
|
||||||
|
|
||||||
// WrappedAssetMaster is a free data retrieval call binding the contract method 0x99da1d3c.
|
// WrappedAssetMaster is a free data retrieval call binding the contract method 0x99da1d3c.
|
||||||
//
|
//
|
||||||
// Solidity: function wrappedAssetMaster() constant returns(address)
|
// Solidity: function wrappedAssetMaster() view returns(address)
|
||||||
func (_Abi *AbiSession) WrappedAssetMaster() (common.Address, error) {
|
func (_Abi *AbiSession) WrappedAssetMaster() (common.Address, error) {
|
||||||
return _Abi.Contract.WrappedAssetMaster(&_Abi.CallOpts)
|
return _Abi.Contract.WrappedAssetMaster(&_Abi.CallOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WrappedAssetMaster is a free data retrieval call binding the contract method 0x99da1d3c.
|
// WrappedAssetMaster is a free data retrieval call binding the contract method 0x99da1d3c.
|
||||||
//
|
//
|
||||||
// Solidity: function wrappedAssetMaster() constant returns(address)
|
// Solidity: function wrappedAssetMaster() view returns(address)
|
||||||
func (_Abi *AbiCallerSession) WrappedAssetMaster() (common.Address, error) {
|
func (_Abi *AbiCallerSession) WrappedAssetMaster() (common.Address, error) {
|
||||||
return _Abi.Contract.WrappedAssetMaster(&_Abi.CallOpts)
|
return _Abi.Contract.WrappedAssetMaster(&_Abi.CallOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WrappedAssets is a free data retrieval call binding the contract method 0xb6694c2a.
|
// WrappedAssets is a free data retrieval call binding the contract method 0xb6694c2a.
|
||||||
//
|
//
|
||||||
// Solidity: function wrappedAssets(bytes32 ) constant returns(address)
|
// Solidity: function wrappedAssets(bytes32 ) view returns(address)
|
||||||
func (_Abi *AbiCaller) WrappedAssets(opts *bind.CallOpts, arg0 [32]byte) (common.Address, error) {
|
func (_Abi *AbiCaller) WrappedAssets(opts *bind.CallOpts, arg0 [32]byte) (common.Address, error) {
|
||||||
var (
|
var (
|
||||||
ret0 = new(common.Address)
|
ret0 = new(common.Address)
|
||||||
|
@ -348,14 +373,14 @@ func (_Abi *AbiCaller) WrappedAssets(opts *bind.CallOpts, arg0 [32]byte) (common
|
||||||
|
|
||||||
// WrappedAssets is a free data retrieval call binding the contract method 0xb6694c2a.
|
// WrappedAssets is a free data retrieval call binding the contract method 0xb6694c2a.
|
||||||
//
|
//
|
||||||
// Solidity: function wrappedAssets(bytes32 ) constant returns(address)
|
// Solidity: function wrappedAssets(bytes32 ) view returns(address)
|
||||||
func (_Abi *AbiSession) WrappedAssets(arg0 [32]byte) (common.Address, error) {
|
func (_Abi *AbiSession) WrappedAssets(arg0 [32]byte) (common.Address, error) {
|
||||||
return _Abi.Contract.WrappedAssets(&_Abi.CallOpts, arg0)
|
return _Abi.Contract.WrappedAssets(&_Abi.CallOpts, arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WrappedAssets is a free data retrieval call binding the contract method 0xb6694c2a.
|
// WrappedAssets is a free data retrieval call binding the contract method 0xb6694c2a.
|
||||||
//
|
//
|
||||||
// Solidity: function wrappedAssets(bytes32 ) constant returns(address)
|
// Solidity: function wrappedAssets(bytes32 ) view returns(address)
|
||||||
func (_Abi *AbiCallerSession) WrappedAssets(arg0 [32]byte) (common.Address, error) {
|
func (_Abi *AbiCallerSession) WrappedAssets(arg0 [32]byte) (common.Address, error) {
|
||||||
return _Abi.Contract.WrappedAssets(&_Abi.CallOpts, arg0)
|
return _Abi.Contract.WrappedAssets(&_Abi.CallOpts, arg0)
|
||||||
}
|
}
|
||||||
|
@ -383,21 +408,21 @@ func (_Abi *AbiTransactorSession) LockAssets(asset common.Address, amount *big.I
|
||||||
|
|
||||||
// LockETH is a paid mutator transaction binding the contract method 0x58d62e46.
|
// LockETH is a paid mutator transaction binding the contract method 0x58d62e46.
|
||||||
//
|
//
|
||||||
// Solidity: function lockETH(bytes32 recipient, uint8 target_chain, uint32 nonce) returns()
|
// Solidity: function lockETH(bytes32 recipient, uint8 target_chain, uint32 nonce) payable returns()
|
||||||
func (_Abi *AbiTransactor) LockETH(opts *bind.TransactOpts, recipient [32]byte, target_chain uint8, nonce uint32) (*types.Transaction, error) {
|
func (_Abi *AbiTransactor) LockETH(opts *bind.TransactOpts, recipient [32]byte, target_chain uint8, nonce uint32) (*types.Transaction, error) {
|
||||||
return _Abi.contract.Transact(opts, "lockETH", recipient, target_chain, nonce)
|
return _Abi.contract.Transact(opts, "lockETH", recipient, target_chain, nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LockETH is a paid mutator transaction binding the contract method 0x58d62e46.
|
// LockETH is a paid mutator transaction binding the contract method 0x58d62e46.
|
||||||
//
|
//
|
||||||
// Solidity: function lockETH(bytes32 recipient, uint8 target_chain, uint32 nonce) returns()
|
// Solidity: function lockETH(bytes32 recipient, uint8 target_chain, uint32 nonce) payable returns()
|
||||||
func (_Abi *AbiSession) LockETH(recipient [32]byte, target_chain uint8, nonce uint32) (*types.Transaction, error) {
|
func (_Abi *AbiSession) LockETH(recipient [32]byte, target_chain uint8, nonce uint32) (*types.Transaction, error) {
|
||||||
return _Abi.Contract.LockETH(&_Abi.TransactOpts, recipient, target_chain, nonce)
|
return _Abi.Contract.LockETH(&_Abi.TransactOpts, recipient, target_chain, nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LockETH is a paid mutator transaction binding the contract method 0x58d62e46.
|
// LockETH is a paid mutator transaction binding the contract method 0x58d62e46.
|
||||||
//
|
//
|
||||||
// Solidity: function lockETH(bytes32 recipient, uint8 target_chain, uint32 nonce) returns()
|
// Solidity: function lockETH(bytes32 recipient, uint8 target_chain, uint32 nonce) payable returns()
|
||||||
func (_Abi *AbiTransactorSession) LockETH(recipient [32]byte, target_chain uint8, nonce uint32) (*types.Transaction, error) {
|
func (_Abi *AbiTransactorSession) LockETH(recipient [32]byte, target_chain uint8, nonce uint32) (*types.Transaction, error) {
|
||||||
return _Abi.Contract.LockETH(&_Abi.TransactOpts, recipient, target_chain, nonce)
|
return _Abi.Contract.LockETH(&_Abi.TransactOpts, recipient, target_chain, nonce)
|
||||||
}
|
}
|
||||||
|
@ -423,6 +448,48 @@ func (_Abi *AbiTransactorSession) SubmitVAA(vaa []byte) (*types.Transaction, err
|
||||||
return _Abi.Contract.SubmitVAA(&_Abi.TransactOpts, vaa)
|
return _Abi.Contract.SubmitVAA(&_Abi.TransactOpts, vaa)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback is a paid mutator transaction binding the contract fallback function.
|
||||||
|
//
|
||||||
|
// Solidity: fallback() payable returns()
|
||||||
|
func (_Abi *AbiTransactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) {
|
||||||
|
return _Abi.contract.RawTransact(opts, calldata)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback is a paid mutator transaction binding the contract fallback function.
|
||||||
|
//
|
||||||
|
// Solidity: fallback() payable returns()
|
||||||
|
func (_Abi *AbiSession) Fallback(calldata []byte) (*types.Transaction, error) {
|
||||||
|
return _Abi.Contract.Fallback(&_Abi.TransactOpts, calldata)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback is a paid mutator transaction binding the contract fallback function.
|
||||||
|
//
|
||||||
|
// Solidity: fallback() payable returns()
|
||||||
|
func (_Abi *AbiTransactorSession) Fallback(calldata []byte) (*types.Transaction, error) {
|
||||||
|
return _Abi.Contract.Fallback(&_Abi.TransactOpts, calldata)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive is a paid mutator transaction binding the contract receive function.
|
||||||
|
//
|
||||||
|
// Solidity: receive() payable returns()
|
||||||
|
func (_Abi *AbiTransactor) Receive(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _Abi.contract.RawTransact(opts, nil) // calldata is disallowed for receive function
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive is a paid mutator transaction binding the contract receive function.
|
||||||
|
//
|
||||||
|
// Solidity: receive() payable returns()
|
||||||
|
func (_Abi *AbiSession) Receive() (*types.Transaction, error) {
|
||||||
|
return _Abi.Contract.Receive(&_Abi.TransactOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive is a paid mutator transaction binding the contract receive function.
|
||||||
|
//
|
||||||
|
// Solidity: receive() payable returns()
|
||||||
|
func (_Abi *AbiTransactorSession) Receive() (*types.Transaction, error) {
|
||||||
|
return _Abi.Contract.Receive(&_Abi.TransactOpts)
|
||||||
|
}
|
||||||
|
|
||||||
// AbiLogGuardianSetChangedIterator is returned from FilterLogGuardianSetChanged and is used to iterate over the raw logs and unpacked data for LogGuardianSetChanged events raised by the Abi contract.
|
// AbiLogGuardianSetChangedIterator is returned from FilterLogGuardianSetChanged and is used to iterate over the raw logs and unpacked data for LogGuardianSetChanged events raised by the Abi contract.
|
||||||
type AbiLogGuardianSetChangedIterator struct {
|
type AbiLogGuardianSetChangedIterator struct {
|
||||||
Event *AbiLogGuardianSetChanged // Event containing the contract specifics and raw log
|
Event *AbiLogGuardianSetChanged // Event containing the contract specifics and raw log
|
||||||
|
@ -626,19 +693,20 @@ func (it *AbiLogTokensLockedIterator) Close() error {
|
||||||
|
|
||||||
// AbiLogTokensLocked represents a LogTokensLocked event raised by the Abi contract.
|
// AbiLogTokensLocked represents a LogTokensLocked event raised by the Abi contract.
|
||||||
type AbiLogTokensLocked struct {
|
type AbiLogTokensLocked struct {
|
||||||
TargetChain uint8
|
TargetChain uint8
|
||||||
TokenChain uint8
|
TokenChain uint8
|
||||||
Token [32]byte
|
TokenDecimals uint8
|
||||||
Sender [32]byte
|
Token [32]byte
|
||||||
Recipient [32]byte
|
Sender [32]byte
|
||||||
Amount *big.Int
|
Recipient [32]byte
|
||||||
Nonce uint32
|
Amount *big.Int
|
||||||
Raw types.Log // Blockchain specific contextual infos
|
Nonce uint32
|
||||||
|
Raw types.Log // Blockchain specific contextual infos
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilterLogTokensLocked is a free log retrieval operation binding the contract event 0x5742f26a345471409566883d5cac5a7d295eee7092e5be3a7d6c60bc2a3e2420.
|
// FilterLogTokensLocked is a free log retrieval operation binding the contract event 0x6bbd554ad75919f71fd91bf917ca6e4f41c10f03ab25751596a22253bb39aab8.
|
||||||
//
|
//
|
||||||
// Solidity: event LogTokensLocked(uint8 target_chain, uint8 token_chain, bytes32 indexed token, bytes32 indexed sender, bytes32 recipient, uint256 amount, uint32 nonce)
|
// Solidity: event LogTokensLocked(uint8 target_chain, uint8 token_chain, uint8 token_decimals, bytes32 indexed token, bytes32 indexed sender, bytes32 recipient, uint256 amount, uint32 nonce)
|
||||||
func (_Abi *AbiFilterer) FilterLogTokensLocked(opts *bind.FilterOpts, token [][32]byte, sender [][32]byte) (*AbiLogTokensLockedIterator, error) {
|
func (_Abi *AbiFilterer) FilterLogTokensLocked(opts *bind.FilterOpts, token [][32]byte, sender [][32]byte) (*AbiLogTokensLockedIterator, error) {
|
||||||
|
|
||||||
var tokenRule []interface{}
|
var tokenRule []interface{}
|
||||||
|
@ -657,9 +725,9 @@ func (_Abi *AbiFilterer) FilterLogTokensLocked(opts *bind.FilterOpts, token [][3
|
||||||
return &AbiLogTokensLockedIterator{contract: _Abi.contract, event: "LogTokensLocked", logs: logs, sub: sub}, nil
|
return &AbiLogTokensLockedIterator{contract: _Abi.contract, event: "LogTokensLocked", logs: logs, sub: sub}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WatchLogTokensLocked is a free log subscription operation binding the contract event 0x5742f26a345471409566883d5cac5a7d295eee7092e5be3a7d6c60bc2a3e2420.
|
// WatchLogTokensLocked is a free log subscription operation binding the contract event 0x6bbd554ad75919f71fd91bf917ca6e4f41c10f03ab25751596a22253bb39aab8.
|
||||||
//
|
//
|
||||||
// Solidity: event LogTokensLocked(uint8 target_chain, uint8 token_chain, bytes32 indexed token, bytes32 indexed sender, bytes32 recipient, uint256 amount, uint32 nonce)
|
// Solidity: event LogTokensLocked(uint8 target_chain, uint8 token_chain, uint8 token_decimals, bytes32 indexed token, bytes32 indexed sender, bytes32 recipient, uint256 amount, uint32 nonce)
|
||||||
func (_Abi *AbiFilterer) WatchLogTokensLocked(opts *bind.WatchOpts, sink chan<- *AbiLogTokensLocked, token [][32]byte, sender [][32]byte) (event.Subscription, error) {
|
func (_Abi *AbiFilterer) WatchLogTokensLocked(opts *bind.WatchOpts, sink chan<- *AbiLogTokensLocked, token [][32]byte, sender [][32]byte) (event.Subscription, error) {
|
||||||
|
|
||||||
var tokenRule []interface{}
|
var tokenRule []interface{}
|
||||||
|
@ -703,9 +771,9 @@ func (_Abi *AbiFilterer) WatchLogTokensLocked(opts *bind.WatchOpts, sink chan<-
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseLogTokensLocked is a log parse operation binding the contract event 0x5742f26a345471409566883d5cac5a7d295eee7092e5be3a7d6c60bc2a3e2420.
|
// ParseLogTokensLocked is a log parse operation binding the contract event 0x6bbd554ad75919f71fd91bf917ca6e4f41c10f03ab25751596a22253bb39aab8.
|
||||||
//
|
//
|
||||||
// Solidity: event LogTokensLocked(uint8 target_chain, uint8 token_chain, bytes32 indexed token, bytes32 indexed sender, bytes32 recipient, uint256 amount, uint32 nonce)
|
// Solidity: event LogTokensLocked(uint8 target_chain, uint8 token_chain, uint8 token_decimals, bytes32 indexed token, bytes32 indexed sender, bytes32 recipient, uint256 amount, uint32 nonce)
|
||||||
func (_Abi *AbiFilterer) ParseLogTokensLocked(log types.Log) (*AbiLogTokensLocked, error) {
|
func (_Abi *AbiFilterer) ParseLogTokensLocked(log types.Log) (*AbiLogTokensLocked, error) {
|
||||||
event := new(AbiLogTokensLocked)
|
event := new(AbiLogTokensLocked)
|
||||||
if err := _Abi.contract.UnpackLog(event, "LogTokensLocked", log); err != nil {
|
if err := _Abi.contract.UnpackLog(event, "LogTokensLocked", log); err != nil {
|
||||||
|
|
|
@ -114,6 +114,7 @@ func (e *EthBridgeWatcher) Run(ctx context.Context) error {
|
||||||
TargetChain: vaa.ChainID(ev.TargetChain),
|
TargetChain: vaa.ChainID(ev.TargetChain),
|
||||||
TokenChain: vaa.ChainID(ev.TokenChain),
|
TokenChain: vaa.ChainID(ev.TokenChain),
|
||||||
TokenAddress: ev.Token,
|
TokenAddress: ev.Token,
|
||||||
|
TokenDecimals: ev.TokenDecimals,
|
||||||
Amount: ev.Amount,
|
Amount: ev.Amount,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,16 +32,6 @@ func NewSolanaBridgeWatcher(url string, lockEvents chan *common.ChainLock, vaaQu
|
||||||
return &SolanaBridgeWatcher{url: url, lockChan: lockEvents, vaaChan: vaaQueue}
|
return &SolanaBridgeWatcher{url: url, lockChan: lockEvents, vaaChan: vaaQueue}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: document/deduplicate
|
|
||||||
func padAddress(address eth_common.Address) vaa.Address {
|
|
||||||
paddedAddress := eth_common.LeftPadBytes(address[:], 32)
|
|
||||||
|
|
||||||
addr := vaa.Address{}
|
|
||||||
copy(addr[:], paddedAddress)
|
|
||||||
|
|
||||||
return addr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *SolanaBridgeWatcher) Run(ctx context.Context) error {
|
func (e *SolanaBridgeWatcher) Run(ctx context.Context) error {
|
||||||
timeout, cancel := context.WithTimeout(ctx, 15*time.Second)
|
timeout, cancel := context.WithTimeout(ctx, 15*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
@ -75,20 +65,21 @@ func (e *SolanaBridgeWatcher) Run(ctx context.Context) error {
|
||||||
switch event := ev.Event.(type) {
|
switch event := ev.Event.(type) {
|
||||||
case *agentv1.LockupEvent_New:
|
case *agentv1.LockupEvent_New:
|
||||||
logger.Debug("received lockup event",
|
logger.Debug("received lockup event",
|
||||||
zap.Any("event", ev)) // TODO: debug level
|
zap.Any("event", ev))
|
||||||
|
|
||||||
lock := &common.ChainLock{
|
lock := &common.ChainLock{
|
||||||
TxHash: eth_common.HexToHash(ev.LockupAddress),
|
TxHash: eth_common.HexToHash(ev.LockupAddress),
|
||||||
Timestamp: time.Time{}, // FIXME
|
Timestamp: time.Unix(int64(ev.Time), 0),
|
||||||
Nonce: event.New.Nonce,
|
Nonce: event.New.Nonce,
|
||||||
SourceAddress: padAddress(eth_common.BytesToAddress(event.New.SourceAddress)),
|
|
||||||
TargetAddress: padAddress(eth_common.BytesToAddress(event.New.TargetAddress)),
|
|
||||||
SourceChain: vaa.ChainIDSolana,
|
SourceChain: vaa.ChainIDSolana,
|
||||||
TargetChain: vaa.ChainID(event.New.TargetChain),
|
TargetChain: vaa.ChainID(event.New.TargetChain),
|
||||||
TokenChain: vaa.ChainID(event.New.TokenChain),
|
TokenChain: vaa.ChainID(event.New.TokenChain),
|
||||||
|
TokenDecimals: uint8(event.New.TokenDecimals),
|
||||||
Amount: new(big.Int).SetBytes(event.New.Amount),
|
Amount: new(big.Int).SetBytes(event.New.Amount),
|
||||||
}
|
}
|
||||||
copy(lock.TokenAddress[:], event.New.TokenAddress)
|
copy(lock.TokenAddress[:], event.New.TokenAddress)
|
||||||
|
copy(lock.SourceAddress[:], event.New.SourceAddress)
|
||||||
|
copy(lock.TargetAddress[:], event.New.TargetAddress)
|
||||||
|
|
||||||
e.lockChan <- lock
|
e.lockChan <- lock
|
||||||
logger.Info("found new lockup transaction", zap.String("lockup_address", ev.LockupAddress))
|
logger.Info("found new lockup transaction", zap.String("lockup_address", ev.LockupAddress))
|
||||||
|
|
|
@ -44,7 +44,7 @@ type (
|
||||||
// Index of the validator
|
// Index of the validator
|
||||||
Index uint8
|
Index uint8
|
||||||
// Signature data
|
// Signature data
|
||||||
Signature [65]byte // TODO: hex marshaller
|
Signature [65]byte // TODO: hex marshaller
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssetMeta describes an asset within the Wormhole protocol
|
// AssetMeta describes an asset within the Wormhole protocol
|
||||||
|
@ -53,6 +53,8 @@ type (
|
||||||
Chain ChainID
|
Chain ChainID
|
||||||
// Address is the address of the token contract/mint/equivalent.
|
// Address is the address of the token contract/mint/equivalent.
|
||||||
Address Address
|
Address Address
|
||||||
|
// Decimals is the number of decimals the token has
|
||||||
|
Decimals uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
vaaBody interface {
|
vaaBody interface {
|
||||||
|
@ -113,8 +115,6 @@ const (
|
||||||
SupportedVAAVersion = 0x01
|
SupportedVAAVersion = 0x01
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Unmarshal deserializes the binary representation of a VAA
|
// Unmarshal deserializes the binary representation of a VAA
|
||||||
func Unmarshal(data []byte) (*VAA, error) {
|
func Unmarshal(data []byte) (*VAA, error) {
|
||||||
if len(data) < minVAALength {
|
if len(data) < minVAALength {
|
||||||
|
@ -321,6 +321,9 @@ func parseBodyTransfer(r io.Reader) (*BodyTransfer, error) {
|
||||||
if n, err := r.Read(b.Asset.Address[:]); err != nil || n != 32 {
|
if n, err := r.Read(b.Asset.Address[:]); err != nil || n != 32 {
|
||||||
return nil, fmt.Errorf("failed to read asset address: %w", err)
|
return nil, fmt.Errorf("failed to read asset address: %w", err)
|
||||||
}
|
}
|
||||||
|
if err := binary.Read(r, binary.BigEndian, &b.Asset.Decimals); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read asset decimals: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
var amountBytes [32]byte
|
var amountBytes [32]byte
|
||||||
if n, err := r.Read(amountBytes[:]); err != nil || n != 32 {
|
if n, err := r.Read(amountBytes[:]); err != nil || n != 32 {
|
||||||
|
@ -348,6 +351,7 @@ func (v *BodyTransfer) serialize() ([]byte, error) {
|
||||||
}
|
}
|
||||||
MustWrite(buf, binary.BigEndian, v.Asset.Chain)
|
MustWrite(buf, binary.BigEndian, v.Asset.Chain)
|
||||||
buf.Write(v.Asset.Address[:])
|
buf.Write(v.Asset.Address[:])
|
||||||
|
MustWrite(buf, binary.BigEndian, v.Asset.Decimals)
|
||||||
|
|
||||||
if v.Amount == nil {
|
if v.Amount == nil {
|
||||||
return nil, fmt.Errorf("amount is empty")
|
return nil, fmt.Errorf("amount is empty")
|
||||||
|
|
|
@ -125,6 +125,7 @@ followed by:
|
||||||
| 7 | token_program | SplToken | | | ️ | |
|
| 7 | token_program | SplToken | | | ️ | |
|
||||||
| 8 | token | WrappedAsset | | | | ✅ |
|
| 8 | token | WrappedAsset | | | | ✅ |
|
||||||
| 9 | destination | TokenAccount | | ✅ | | |
|
| 9 | destination | TokenAccount | | ✅ | | |
|
||||||
|
| 10 | wrapped_meta | WrappedMeta | | ✅ | opt | ✅ |
|
||||||
|
|
||||||
##### Transfer: Ethereum (wrapped) -> Solana (native)
|
##### Transfer: Ethereum (wrapped) -> Solana (native)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
pragma solidity ^0.6.0;
|
pragma solidity ^0.6.0;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||||
import "@openzeppelin/contracts/math/SafeMath.sol";
|
import "@openzeppelin/contracts/math/SafeMath.sol";
|
||||||
|
@ -40,6 +41,7 @@ contract Wormhole is ReentrancyGuard {
|
||||||
event LogTokensLocked(
|
event LogTokensLocked(
|
||||||
uint8 target_chain,
|
uint8 target_chain,
|
||||||
uint8 token_chain,
|
uint8 token_chain,
|
||||||
|
uint8 token_decimals,
|
||||||
bytes32 indexed token,
|
bytes32 indexed token,
|
||||||
bytes32 indexed sender,
|
bytes32 indexed sender,
|
||||||
bytes32 recipient,
|
bytes32 recipient,
|
||||||
|
@ -56,7 +58,7 @@ contract Wormhole is ReentrancyGuard {
|
||||||
uint32 public guardian_set_expirity;
|
uint32 public guardian_set_expirity;
|
||||||
|
|
||||||
// Mapping of already consumedVAAs
|
// Mapping of already consumedVAAs
|
||||||
mapping(bytes32 => bool) consumedVAAs;
|
mapping(bytes32 => bool) public consumedVAAs;
|
||||||
|
|
||||||
// Mapping of wrapped asset ERC20 contracts
|
// Mapping of wrapped asset ERC20 contracts
|
||||||
mapping(bytes32 => address) public wrappedAssets;
|
mapping(bytes32 => address) public wrappedAssets;
|
||||||
|
@ -88,7 +90,7 @@ contract Wormhole is ReentrancyGuard {
|
||||||
uint offset = 6 + 66 * len_signers;
|
uint offset = 6 + 66 * len_signers;
|
||||||
|
|
||||||
// Load 4 bytes timestamp
|
// Load 4 bytes timestamp
|
||||||
uint32 timestamp = vaa.toUint32(offset);
|
//uint32 timestamp = vaa.toUint32(offset);
|
||||||
|
|
||||||
// Hash the body
|
// Hash the body
|
||||||
bytes32 hash = keccak256(vaa.slice(offset, vaa.length - offset));
|
bytes32 hash = keccak256(vaa.slice(offset, vaa.length - offset));
|
||||||
|
@ -155,7 +157,7 @@ contract Wormhole is ReentrancyGuard {
|
||||||
|
|
||||||
uint8 token_chain = data.toUint8(70);
|
uint8 token_chain = data.toUint8(70);
|
||||||
//bytes32 token_address = data.toBytes32(71);
|
//bytes32 token_address = data.toBytes32(71);
|
||||||
uint256 amount = data.toUint256(103);
|
uint256 amount = data.toUint256(104);
|
||||||
|
|
||||||
require(source_chain != target_chain, "same chain transfers are not supported");
|
require(source_chain != target_chain, "same chain transfers are not supported");
|
||||||
require(target_chain == CHAIN_ID, "transfer must be incoming");
|
require(target_chain == CHAIN_ID, "transfer must be incoming");
|
||||||
|
@ -168,7 +170,8 @@ contract Wormhole is ReentrancyGuard {
|
||||||
// if no: create and mint
|
// if no: create and mint
|
||||||
address wrapped_asset = wrappedAssets[asset_id];
|
address wrapped_asset = wrappedAssets[asset_id];
|
||||||
if (wrapped_asset == address(0)) {
|
if (wrapped_asset == address(0)) {
|
||||||
wrapped_asset = deployWrappedAsset(asset_id, token_chain, token_address);
|
uint8 asset_decimals = data.toUint8(103);
|
||||||
|
wrapped_asset = deployWrappedAsset(asset_id, token_chain, token_address, asset_decimals);
|
||||||
}
|
}
|
||||||
|
|
||||||
WrappedAsset(wrapped_asset).mint(target_address, amount);
|
WrappedAsset(wrapped_asset).mint(target_address, amount);
|
||||||
|
@ -179,7 +182,7 @@ contract Wormhole is ReentrancyGuard {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function deployWrappedAsset(bytes32 seed, uint8 token_chain, bytes32 token_address) private returns (address asset){
|
function deployWrappedAsset(bytes32 seed, uint8 token_chain, bytes32 token_address, uint8 decimals) private returns (address asset){
|
||||||
// Taken from https://github.com/OpenZeppelin/openzeppelin-sdk/blob/master/packages/lib/contracts/upgradeability/ProxyFactory.sol
|
// Taken from https://github.com/OpenZeppelin/openzeppelin-sdk/blob/master/packages/lib/contracts/upgradeability/ProxyFactory.sol
|
||||||
// Licensed under MIT
|
// Licensed under MIT
|
||||||
bytes20 targetBytes = bytes20(wrappedAssetMaster);
|
bytes20 targetBytes = bytes20(wrappedAssetMaster);
|
||||||
|
@ -192,7 +195,7 @@ contract Wormhole is ReentrancyGuard {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call initializer
|
// Call initializer
|
||||||
WrappedAsset(asset).initialize(token_chain, token_address);
|
WrappedAsset(asset).initialize(token_chain, token_address, decimals);
|
||||||
|
|
||||||
// Store address
|
// Store address
|
||||||
wrappedAssets[seed] = asset;
|
wrappedAssets[seed] = asset;
|
||||||
|
@ -225,7 +228,7 @@ contract Wormhole is ReentrancyGuard {
|
||||||
asset_address = bytes32(uint256(asset));
|
asset_address = bytes32(uint256(asset));
|
||||||
}
|
}
|
||||||
|
|
||||||
emit LogTokensLocked(target_chain, asset_chain, asset_address, bytes32(uint256(msg.sender)), recipient, amount, nonce);
|
emit LogTokensLocked(target_chain, asset_chain, ERC20(asset).decimals(), asset_address, bytes32(uint256(msg.sender)), recipient, amount, nonce);
|
||||||
}
|
}
|
||||||
|
|
||||||
function lockETH(
|
function lockETH(
|
||||||
|
@ -239,7 +242,7 @@ contract Wormhole is ReentrancyGuard {
|
||||||
WETH(WETHAddress).deposit{value : msg.value}();
|
WETH(WETHAddress).deposit{value : msg.value}();
|
||||||
|
|
||||||
// Log deposit of WETH
|
// Log deposit of WETH
|
||||||
emit LogTokensLocked(target_chain, CHAIN_ID, bytes32(uint256(WETHAddress)), bytes32(uint256(msg.sender)), recipient, msg.value, nonce);
|
emit LogTokensLocked(target_chain, CHAIN_ID, 18, bytes32(uint256(WETHAddress)), bytes32(uint256(msg.sender)), recipient, msg.value, nonce);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ contract WrappedAsset is IERC20, Context {
|
||||||
bool public initialized;
|
bool public initialized;
|
||||||
address public bridge;
|
address public bridge;
|
||||||
|
|
||||||
function initialize(uint8 _assetChain, bytes32 _assetAddress) public {
|
function initialize(uint8 _assetChain, bytes32 _assetAddress, uint8 decimals) public {
|
||||||
require(!initialized, "already initialized");
|
require(!initialized, "already initialized");
|
||||||
// Set local fields
|
// Set local fields
|
||||||
assetChain = _assetChain;
|
assetChain = _assetChain;
|
||||||
|
@ -23,7 +23,7 @@ contract WrappedAsset is IERC20, Context {
|
||||||
|
|
||||||
_name = "Wormhole Wrapped";
|
_name = "Wormhole Wrapped";
|
||||||
_symbol = "WWT";
|
_symbol = "WWT";
|
||||||
_decimals = 18;
|
_decimals = decimals;
|
||||||
}
|
}
|
||||||
|
|
||||||
function mint(address account, uint256 amount) external {
|
function mint(address account, uint256 amount) external {
|
||||||
|
|
|
@ -58,12 +58,11 @@ contract("Wormhole", function () {
|
||||||
let bridge = await Wormhole.deployed();
|
let bridge = await Wormhole.deployed();
|
||||||
|
|
||||||
// User locked an asset on the foreign chain and the VAA proving this is transferred in.
|
// User locked an asset on the foreign chain and the VAA proving this is transferred in.
|
||||||
await bridge.submitVAA("0x01000000000100eecb367540286d326e333ea06542b82d3feaeb0dc33b1b14bda8cdf8287da2a630a9a6692112bcedee501c0947c607081d51426fa1982ef342c07f4502b584c801000007d010000000380102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e99880000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
await bridge.submitVAA("0x01000000000100454e7de661cd4386b1ce598a505825f8ed66fbc6a608393bae6257fef7370da27a2068240a902470bed6c0b1fa23d38e5d5958e2a422d59a0217fbe155638ed600000007d010000000380102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e9988080000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
||||||
// Expect user to have a balance of a new wrapped asset
|
// Expect user to have a balance of a new wrapped asset
|
||||||
|
|
||||||
// submitVAA has automatically created a new WrappedAsset for the foreign asset that has been transferred in.
|
// submitVAA has automatically created a new WrappedAsset for the foreign asset that has been transferred in.
|
||||||
// We know the address because deterministic network. A user would see the address in the submitVAA tx log.
|
// We know the address because deterministic network. A user would see the address in the submitVAA tx log.
|
||||||
let wa = new WrappedAsset("0x3c63250aFA2470359482d98749f2d60D2971c818");
|
let wa = new WrappedAsset("0xC3697aaf5B3D354214548248710414812099bc93");
|
||||||
assert.equal(await wa.assetChain(), 1)
|
assert.equal(await wa.assetChain(), 1)
|
||||||
// Remote asset's contract address.
|
// Remote asset's contract address.
|
||||||
assert.equal(await wa.assetAddress(), "0x0000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e9988")
|
assert.equal(await wa.assetAddress(), "0x0000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e9988")
|
||||||
|
@ -75,7 +74,7 @@ contract("Wormhole", function () {
|
||||||
it("should not accept the same VAA twice", async function () {
|
it("should not accept the same VAA twice", async function () {
|
||||||
let bridge = await Wormhole.deployed();
|
let bridge = await Wormhole.deployed();
|
||||||
try {
|
try {
|
||||||
await bridge.submitVAA("0x01000000000100eecb367540286d326e333ea06542b82d3feaeb0dc33b1b14bda8cdf8287da2a630a9a6692112bcedee501c0947c607081d51426fa1982ef342c07f4502b584c801000007d010000000380102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e99880000000000000000000000000000000000000000000000000de0b6b3a7640000");
|
await bridge.submitVAA("0x01000000000100454e7de661cd4386b1ce598a505825f8ed66fbc6a608393bae6257fef7370da27a2068240a902470bed6c0b1fa23d38e5d5958e2a422d59a0217fbe155638ed600000007d010000000380102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e9988080000000000000000000000000000000000000000000000000de0b6b3a7640000");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
assert.equal(e.reason, "VAA was already executed")
|
assert.equal(e.reason, "VAA was already executed")
|
||||||
return
|
return
|
||||||
|
@ -86,7 +85,7 @@ contract("Wormhole", function () {
|
||||||
it("should burn tokens on lock", async function () {
|
it("should burn tokens on lock", async function () {
|
||||||
let bridge = await Wormhole.deployed();
|
let bridge = await Wormhole.deployed();
|
||||||
// Expect user to have a balance
|
// Expect user to have a balance
|
||||||
let wa = new WrappedAsset("0x3c63250aFA2470359482d98749f2d60D2971c818")
|
let wa = new WrappedAsset("0xC3697aaf5B3D354214548248710414812099bc93")
|
||||||
|
|
||||||
await bridge.lockAssets(wa.address, "500000000000000000", "0x0", 2, 2);
|
await bridge.lockAssets(wa.address, "500000000000000000", "0x0", 2, 2);
|
||||||
let balance = await wa.balanceOf("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1");
|
let balance = await wa.balanceOf("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1");
|
||||||
|
@ -113,7 +112,7 @@ contract("Wormhole", function () {
|
||||||
// Transfer of that token out of the contract should not work
|
// Transfer of that token out of the contract should not work
|
||||||
let threw = false;
|
let threw = false;
|
||||||
try {
|
try {
|
||||||
await bridge.submitVAA("0x01000000000100e2d8610a6cf10587ba2e4f43dc639eeacd3fb4297338955b00b7653094278082505de82418b9e0925d9dd889d0850252aa7c613e63c8b2c27ff22c0001c6336600000007d010000000380102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c102000000000000000000000000d833215cbcc3f914bd1c9ece3ee7bf8b14f841bb0000000000000000000000000000000000000000000000000de0b6b3a7640000");
|
await bridge.submitVAA("0x01000000000100078f0fe9406808b1e5003867ab74aa2085153b7735b329640d275ea943dd115d00e356c6d343142d9190872c11d2de898d075cea7f4e85ff2188af299e26a14200000007d010000000380102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c102000000000000000000000000d833215cbcc3f914bd1c9ece3ee7bf8b14f841bb080000000000000000000000000000000000000000000000000de0b6b3a7640000");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
threw = true;
|
threw = true;
|
||||||
}
|
}
|
||||||
|
@ -137,7 +136,7 @@ contract("Wormhole", function () {
|
||||||
assert.equal(await token.balanceOf(bridge.address), "1000000000000000000");
|
assert.equal(await token.balanceOf(bridge.address), "1000000000000000000");
|
||||||
|
|
||||||
// Transfer this token back
|
// Transfer this token back
|
||||||
await bridge.submitVAA("0x01000000000100e2d8610a6cf10587ba2e4f43dc639eeacd3fb4297338955b00b7653094278082505de82418b9e0925d9dd889d0850252aa7c613e63c8b2c27ff22c0001c6336600000007d010000000380102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c102000000000000000000000000d833215cbcc3f914bd1c9ece3ee7bf8b14f841bb0000000000000000000000000000000000000000000000000de0b6b3a7640000");
|
await bridge.submitVAA("0x01000000000100078f0fe9406808b1e5003867ab74aa2085153b7735b329640d275ea943dd115d00e356c6d343142d9190872c11d2de898d075cea7f4e85ff2188af299e26a14200000007d010000000380102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c102000000000000000000000000d833215cbcc3f914bd1c9ece3ee7bf8b14f841bb080000000000000000000000000000000000000000000000000de0b6b3a7640000");
|
||||||
assert.equal(await token.balanceOf("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"), "1000000000000000000");
|
assert.equal(await token.balanceOf("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"), "1000000000000000000");
|
||||||
assert.equal(await token.balanceOf(bridge.address), "0");
|
assert.equal(await token.balanceOf(bridge.address), "0");
|
||||||
});
|
});
|
||||||
|
@ -173,14 +172,14 @@ contract("Wormhole", function () {
|
||||||
let bridge = await Wormhole.deployed();
|
let bridge = await Wormhole.deployed();
|
||||||
|
|
||||||
// Test VAA from guardian set 0; timestamp 1000
|
// Test VAA from guardian set 0; timestamp 1000
|
||||||
await bridge.submitVAA("0x0100000000010000b61ecc7b9de12de6fc7f01d8a89f8c2911329e44198d0a47768344c69eadd510fd5ab6474a24aa11a6751465fb4e2f8c81a4dbc2fc2427b4c5a981e8e63ed900000003e810000000380102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e99880000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
await bridge.submitVAA("0x0100000000010034890d1c1aa2455d083602996d924ca9ba2fd9641dcdaa3b0811c9ed37e831a8433b40b0f0779fa16be2daaf53ede378530a135b68ac95814c9d25023a29580e01000003e810000000380102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e9988080000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
||||||
|
|
||||||
await advanceTimeAndBlock(1000);
|
await advanceTimeAndBlock(1000);
|
||||||
|
|
||||||
// Test VAA from guardian set 0; timestamp 2000 - should not work anymore
|
// Test VAA from guardian set 0; timestamp 2000 - should not work anymore
|
||||||
let threw = false;
|
let threw = false;
|
||||||
try {
|
try {
|
||||||
await bridge.submitVAA("0x01000000000100b7e82826980fb9f2389a6e22f7db12d5872e7900775c2c5d6ad1c3558ee7a1314f0f7f171cad73caaac0599c8914009ea9d0ef0e416404b141f844b85a5d254701000007d010000000380102020105000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e99880000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
await bridge.submitVAA("0x010000000001005a55b73ff79bc3cc39bec075ae28ae8351eee1428a7701f0d47fec5736bcfd9e158b49e6282678c425aed8185233ea4ef033af33bd450a77a46ddbadf3ea09ba00000007d010000000380102020105000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e9988080000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
threw = true;
|
threw = true;
|
||||||
assert.equal(e.reason, "guardian set has expired")
|
assert.equal(e.reason, "guardian set has expired")
|
||||||
|
@ -188,7 +187,7 @@ contract("Wormhole", function () {
|
||||||
assert.isTrue(threw, "guardian set did not expire")
|
assert.isTrue(threw, "guardian set did not expire")
|
||||||
|
|
||||||
// Test same transaction with guardian set 1; timestamp 2000
|
// Test same transaction with guardian set 1; timestamp 2000
|
||||||
await bridge.submitVAA("0x01000000010100a3f58fb72b3c7e242d6934718eafb3076cb0764e65d8df3e0746b0c72cca791027ac649fa0095a1c3537611f4adc0dc90aaa01fce31fac722eae898cfb06e96d01000007d010000000380102020105000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e99880000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
await bridge.submitVAA("0x01000000010100958a39752b14ab62a3dcdb37a8642c4ca1085c6ac77205a462ee5bb3650c92407675729615f69255fc150835621e96c917e68929efb975db9647636543c710f200000007d010000000380102020105000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e9988080000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
||||||
});
|
});
|
||||||
|
|
||||||
it("mismatching guardian set and signature should not work", async function () {
|
it("mismatching guardian set and signature should not work", async function () {
|
||||||
|
@ -197,7 +196,7 @@ contract("Wormhole", function () {
|
||||||
// Test VAA signed by guardian set 0 but set guardian set index to 1
|
// Test VAA signed by guardian set 0 but set guardian set index to 1
|
||||||
let threw = false;
|
let threw = false;
|
||||||
try {
|
try {
|
||||||
await bridge.submitVAA("0x010000000101006f84df72f3f935543e9bda60d92f77e2e2c073655311f3fc00518bbe7e054ff87e5e6e3c9df9e5bd756ee033253d4513ddebf03ff844fdc0f48f7dcc1b3fd6e10000000fa01087000000370102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000009561c133dd8580860b6b7e504bc5aa500f0f06a70000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
await bridge.submitVAA("0x01000000010100724a1d2cda45da3cf38f9e0eaef01742210f4deabf9b9d4b20127f6a200a94805928e26ae5f5ab8c3e1cb6d5231d4c48aacae0841513fbd3d9d430be7145db8200000007d010000000390102020105000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e9988080000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
threw = true;
|
threw = true;
|
||||||
assert.equal(e.reason, "VAA signature invalid")
|
assert.equal(e.reason, "VAA signature invalid")
|
||||||
|
@ -214,7 +213,7 @@ contract("Wormhole", function () {
|
||||||
// Test VAA signed by only 3 signers
|
// Test VAA signed by only 3 signers
|
||||||
let threw = false;
|
let threw = false;
|
||||||
try {
|
try {
|
||||||
await bridge.submitVAA("0x010000000203001dbccdb06c91929042b20a136d226890e22b07120d2854aa5c17bc1cce934cf66e2f5e31a3d883bc928346c35352a7627fb0aa7e420b73a89dc0c205780f98bc0001eadd27047cb0988ed4a7c681af758e88c628f2a3c424186044e3fd9ad8c3425f401bfc29674db720f62f08a251ff6aa3b982adb57186422cdad03cc4bfc07bb001020193d92acf2ecadad96273f122ada995700225c18d65db636db7f52e2c77906e3e0153a163c4d123b68f78cc1a8c5dbd4bdf1a26718cfc850c8278ec4a39bb470100000fa010000000390102020105000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e99880000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
await bridge.submitVAA("0x01000000020300e94bec8a17bd313522cdfea30cec5406a41a4cc4b6ec416a633ebe3aca070ae448e370e0a2e7c67fed04a2b825f56cf226c76e6ecd2e71865642393bf729dad80101ccf89506bef58d8cb12baabd60e3304cfb90ef0ef0657caba9c37ffa0d34a54c3aacd1a475ef4c72f24e8d9ce1e2de51e580ce85b18356436b6cda9e2ae9abc0010285f0d3c0d1cd421ce0ae515db1ac3b623c17d4702564971932fb9925c0506fc76e43a7283c712ee680cf058c3c447653c352ca9827b1780e1fc88a61540092d90100000fa010000000390102020105000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e9988080000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
threw = true;
|
threw = true;
|
||||||
assert.equal(e.reason, "no quorum")
|
assert.equal(e.reason, "no quorum")
|
||||||
|
@ -222,6 +221,6 @@ contract("Wormhole", function () {
|
||||||
assert.isTrue(threw, "accepted only 3 signatures")
|
assert.isTrue(threw, "accepted only 3 signatures")
|
||||||
|
|
||||||
// Test VAA signed by 5 signers (all except i=3)
|
// Test VAA signed by 5 signers (all except i=3)
|
||||||
await bridge.submitVAA("0x010000000205001dbccdb06c91929042b20a136d226890e22b07120d2854aa5c17bc1cce934cf66e2f5e31a3d883bc928346c35352a7627fb0aa7e420b73a89dc0c205780f98bc0001eadd27047cb0988ed4a7c681af758e88c628f2a3c424186044e3fd9ad8c3425f401bfc29674db720f62f08a251ff6aa3b982adb57186422cdad03cc4bfc07bb0010393f4821a0fc8248ad8eccfb6e1b6a1fb70d0294a6a2b53cb6e222205f3d9f960491fdda4e23e2dde46b084f4ac101050deecbe871eeec218217037d7974b41a301049571b8d3fbcebad1e868331570120a27cf122d33f3d5b95355fde3712ecdbd5233888ec51e5d9e960beaa9a0697f5ac69f9deae37782b874fbe8aecf064087e00105ddd37a55e2a654f5898b1863eaf8efa464797bfa602893d0bcbcc06269df6a3b4ba88c01f3ad22d23a02c8dc1cb34d28b6eb4dd3e2030b8b42ff6909537faf430000000fa010000000390102020105000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e99880000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
await bridge.submitVAA("0x01000000020500e94bec8a17bd313522cdfea30cec5406a41a4cc4b6ec416a633ebe3aca070ae448e370e0a2e7c67fed04a2b825f56cf226c76e6ecd2e71865642393bf729dad80101ccf89506bef58d8cb12baabd60e3304cfb90ef0ef0657caba9c37ffa0d34a54c3aacd1a475ef4c72f24e8d9ce1e2de51e580ce85b18356436b6cda9e2ae9abc001033e9b4ff5fb545e964e907349e3dab0057c408c832bb31fb76fae7f81c3e488ea4897ce14db61c46d1169bd64b449498b1a18dee4de0ef2038b1c7e3a4a0239a0010432eac9532a4c0ce279d6a3018a5ea0d74402eb6969df5d444f20e0cca66d3b4c53e41cb18648f64af100c7410692e83fa16e5696b1f5f0d517653b003e22689800055859330bd1fee76d99728803fa26d739e494e1a232f5658150c2a2c97e1c9722793bdd83bd7cbb4a39b587b45093ee76187c72dfd68d64b7c0abc32bfef5d55c0000000fa010000000390102020105000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e9988080000000000000000000000000000000000000000000000000de0b6b3a7640000")
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -46,7 +46,8 @@ message LockupEventNew {
|
||||||
bytes targetAddress = 5;
|
bytes targetAddress = 5;
|
||||||
uint32 tokenChain = 6;
|
uint32 tokenChain = 6;
|
||||||
bytes tokenAddress = 7;
|
bytes tokenAddress = 7;
|
||||||
bytes amount = 8;
|
uint32 tokenDecimals = 8;
|
||||||
|
bytes amount = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A VAA was posted to Solana for data availability.
|
// A VAA was posted to Solana for data availability.
|
||||||
|
@ -58,7 +59,8 @@ message LockupEventVAAPosted {
|
||||||
bytes targetAddress = 5;
|
bytes targetAddress = 5;
|
||||||
uint32 tokenChain = 6;
|
uint32 tokenChain = 6;
|
||||||
bytes tokenAddress = 7;
|
bytes tokenAddress = 7;
|
||||||
bytes amount = 8;
|
uint32 tokenDecimals = 8;
|
||||||
|
bytes amount = 9;
|
||||||
|
|
||||||
bytes vaa = 9;
|
bytes vaa = 10;
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,6 +183,7 @@ impl Agent for AgentImpl {
|
||||||
target_address: b.foreign_address.to_vec(),
|
target_address: b.foreign_address.to_vec(),
|
||||||
token_chain: b.asset.chain as u32,
|
token_chain: b.asset.chain as u32,
|
||||||
token_address: b.asset.address.to_vec(),
|
token_address: b.asset.address.to_vec(),
|
||||||
|
token_decimals: b.asset.decimals as u32,
|
||||||
amount: amount_b.to_vec(),
|
amount: amount_b.to_vec(),
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
|
@ -200,6 +201,7 @@ impl Agent for AgentImpl {
|
||||||
target_address: b.foreign_address.to_vec(),
|
target_address: b.foreign_address.to_vec(),
|
||||||
token_chain: b.asset.chain as u32,
|
token_chain: b.asset.chain as u32,
|
||||||
token_address: b.asset.address.to_vec(),
|
token_address: b.asset.address.to_vec(),
|
||||||
|
token_decimals: b.asset.decimals as u32,
|
||||||
amount: amount_b.to_vec(),
|
amount: amount_b.to_vec(),
|
||||||
vaa: b.vaa.to_vec(),
|
vaa: b.vaa.to_vec(),
|
||||||
})),
|
})),
|
||||||
|
|
|
@ -14,7 +14,7 @@ use solana_sdk::{
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::error::Error::VAATooLong;
|
use crate::error::Error::VAATooLong;
|
||||||
use crate::instruction::BridgeInstruction::{CreateWrapped, Initialize, PostVAA, TransferOut};
|
use crate::instruction::BridgeInstruction::{Initialize, PostVAA, TransferOut};
|
||||||
use crate::state::{AssetMeta, Bridge, BridgeConfig};
|
use crate::state::{AssetMeta, Bridge, BridgeConfig};
|
||||||
use crate::vaa::{VAABody, VAA};
|
use crate::vaa::{VAABody, VAA};
|
||||||
|
|
||||||
|
@ -123,9 +123,6 @@ pub enum BridgeInstruction {
|
||||||
/// Deletes a `ExecutedVAA` after the `VAA_EXPIRATION_TIME` is over to free up space on chain.
|
/// Deletes a `ExecutedVAA` after the `VAA_EXPIRATION_TIME` is over to free up space on chain.
|
||||||
/// This returns the rent to the sender.
|
/// This returns the rent to the sender.
|
||||||
EvictClaimedVAA(),
|
EvictClaimedVAA(),
|
||||||
|
|
||||||
/// Creates a new wrapped asset
|
|
||||||
CreateWrapped(AssetMeta),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BridgeInstruction {
|
impl BridgeInstruction {
|
||||||
|
@ -156,11 +153,6 @@ impl BridgeInstruction {
|
||||||
let payload: VAAData = input[1..].to_vec();
|
let payload: VAAData = input[1..].to_vec();
|
||||||
PostVAA(payload)
|
PostVAA(payload)
|
||||||
}
|
}
|
||||||
5 => {
|
|
||||||
let payload: &AssetMeta = unpack(input)?;
|
|
||||||
|
|
||||||
CreateWrapped(*payload)
|
|
||||||
}
|
|
||||||
_ => return Err(ProgramError::InvalidInstructionData),
|
_ => return Err(ProgramError::InvalidInstructionData),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -209,13 +201,6 @@ impl BridgeInstruction {
|
||||||
Self::EvictClaimedVAA() => {
|
Self::EvictClaimedVAA() => {
|
||||||
output[0] = 4;
|
output[0] = 4;
|
||||||
}
|
}
|
||||||
Self::CreateWrapped(meta) => {
|
|
||||||
output[0] = 5;
|
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
|
||||||
let value =
|
|
||||||
unsafe { &mut *(&mut output[size_of::<u8>()] as *mut u8 as *mut AssetMeta) };
|
|
||||||
*value = meta;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
@ -261,38 +246,6 @@ pub fn initialize(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a 'CreateWrapped' instruction.
|
|
||||||
#[cfg(not(target_arch = "bpf"))]
|
|
||||||
pub fn create_wrapped(
|
|
||||||
program_id: &Pubkey,
|
|
||||||
payer: &Pubkey,
|
|
||||||
meta: AssetMeta,
|
|
||||||
) -> Result<Instruction, ProgramError> {
|
|
||||||
let data = BridgeInstruction::CreateWrapped(meta).serialize()?;
|
|
||||||
|
|
||||||
let bridge_key = Bridge::derive_bridge_id(program_id)?;
|
|
||||||
let wrapped_mint_key =
|
|
||||||
Bridge::derive_wrapped_asset_id(program_id, &bridge_key, meta.chain, meta.address)?;
|
|
||||||
let wrapped_meta_key =
|
|
||||||
Bridge::derive_wrapped_meta_id(program_id, &bridge_key, &wrapped_mint_key)?;
|
|
||||||
|
|
||||||
let accounts = vec![
|
|
||||||
AccountMeta::new_readonly(*program_id, false),
|
|
||||||
AccountMeta::new_readonly(solana_sdk::system_program::id(), false),
|
|
||||||
AccountMeta::new_readonly(spl_token::id(), false),
|
|
||||||
AccountMeta::new(bridge_key, false),
|
|
||||||
AccountMeta::new(*payer, true),
|
|
||||||
AccountMeta::new(wrapped_mint_key, false),
|
|
||||||
AccountMeta::new(wrapped_meta_key, false),
|
|
||||||
];
|
|
||||||
|
|
||||||
Ok(Instruction {
|
|
||||||
program_id: *program_id,
|
|
||||||
accounts,
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an 'TransferOut' instruction.
|
/// Creates an 'TransferOut' instruction.
|
||||||
#[cfg(not(target_arch = "bpf"))]
|
#[cfg(not(target_arch = "bpf"))]
|
||||||
pub fn transfer_out(
|
pub fn transfer_out(
|
||||||
|
@ -389,7 +342,6 @@ pub fn post_vaa(
|
||||||
)?;
|
)?;
|
||||||
accounts.push(AccountMeta::new(transfer_key, false))
|
accounts.push(AccountMeta::new(transfer_key, false))
|
||||||
} else if t.asset.chain == CHAIN_ID_SOLANA {
|
} else if t.asset.chain == CHAIN_ID_SOLANA {
|
||||||
println!("kat");
|
|
||||||
// Foreign (wrapped) -> Solana (native)
|
// Foreign (wrapped) -> Solana (native)
|
||||||
let mint_key = Pubkey::new(&t.asset.address);
|
let mint_key = Pubkey::new(&t.asset.address);
|
||||||
let custody_key = Bridge::derive_custody_id(program_id, &bridge_key, &mint_key)?;
|
let custody_key = Bridge::derive_custody_id(program_id, &bridge_key, &mint_key)?;
|
||||||
|
@ -398,7 +350,6 @@ pub fn post_vaa(
|
||||||
accounts.push(AccountMeta::new(Pubkey::new(&t.target_address), false));
|
accounts.push(AccountMeta::new(Pubkey::new(&t.target_address), false));
|
||||||
accounts.push(AccountMeta::new(custody_key, false));
|
accounts.push(AccountMeta::new(custody_key, false));
|
||||||
} else {
|
} else {
|
||||||
println!("kit");
|
|
||||||
// Foreign (native) -> Solana (wrapped)
|
// Foreign (native) -> Solana (wrapped)
|
||||||
let wrapped_key = Bridge::derive_wrapped_asset_id(
|
let wrapped_key = Bridge::derive_wrapped_asset_id(
|
||||||
program_id,
|
program_id,
|
||||||
|
|
|
@ -61,10 +61,6 @@ impl Bridge {
|
||||||
|
|
||||||
Self::process_vaa(program_id, accounts, vaa_body, &vaa, &hash)
|
Self::process_vaa(program_id, accounts, vaa_body, &vaa, &hash)
|
||||||
}
|
}
|
||||||
CreateWrapped(meta) => {
|
|
||||||
info!("Instruction: CreateWrapped");
|
|
||||||
Self::process_create_wrapped(program_id, accounts, &meta)
|
|
||||||
}
|
|
||||||
_ => panic!(""),
|
_ => panic!(""),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,6 +154,7 @@ impl Bridge {
|
||||||
|
|
||||||
let sender = Bridge::token_account_deserialize(sender_account_info)?;
|
let sender = Bridge::token_account_deserialize(sender_account_info)?;
|
||||||
let bridge = Bridge::bridge_deserialize(bridge_info)?;
|
let bridge = Bridge::bridge_deserialize(bridge_info)?;
|
||||||
|
let mint = Bridge::mint_deserialize(mint_info)?;
|
||||||
|
|
||||||
// Does the token belong to the mint
|
// Does the token belong to the mint
|
||||||
if sender.mint != *mint_info.key {
|
if sender.mint != *mint_info.key {
|
||||||
|
@ -196,7 +193,7 @@ impl Bridge {
|
||||||
|
|
||||||
// Load transfer account
|
// Load transfer account
|
||||||
let mut transfer_data = transfer_info.data.borrow_mut();
|
let mut transfer_data = transfer_info.data.borrow_mut();
|
||||||
let mut transfer: &mut TransferOutProposal = Self::unpack(&mut transfer_data)?;
|
let mut transfer: &mut TransferOutProposal = Self::unpack_unchecked(&mut transfer_data)?;
|
||||||
|
|
||||||
// Burn tokens
|
// Burn tokens
|
||||||
Bridge::wrapped_burn(
|
Bridge::wrapped_burn(
|
||||||
|
@ -214,61 +211,13 @@ impl Bridge {
|
||||||
transfer.foreign_address = t.target;
|
transfer.foreign_address = t.target;
|
||||||
transfer.amount = t.amount;
|
transfer.amount = t.amount;
|
||||||
transfer.to_chain_id = t.chain_id;
|
transfer.to_chain_id = t.chain_id;
|
||||||
transfer.asset = t.asset;
|
|
||||||
|
|
||||||
Ok(())
|
// Make sure decimals are correct
|
||||||
}
|
transfer.asset = AssetMeta {
|
||||||
|
chain: t.asset.chain, // Chain and address cannot be spoofed because the account is derived from it
|
||||||
/// Creates a new wrapped asset
|
address: t.asset.address,
|
||||||
pub fn process_create_wrapped(
|
decimals: mint.decimals, // We use the info from mint because it can be spoofed
|
||||||
program_id: &Pubkey,
|
};
|
||||||
accounts: &[AccountInfo],
|
|
||||||
a: &AssetMeta,
|
|
||||||
) -> ProgramResult {
|
|
||||||
info!("create wrapped");
|
|
||||||
let account_info_iter = &mut accounts.iter();
|
|
||||||
next_account_info(account_info_iter)?; // Bridge program
|
|
||||||
next_account_info(account_info_iter)?; // System program
|
|
||||||
next_account_info(account_info_iter)?; // Token program
|
|
||||||
let bridge_info = next_account_info(account_info_iter)?;
|
|
||||||
let payer_info = next_account_info(account_info_iter)?;
|
|
||||||
let mint_info = next_account_info(account_info_iter)?;
|
|
||||||
let wrapped_meta_info = next_account_info(account_info_iter)?;
|
|
||||||
|
|
||||||
let bridge = Bridge::bridge_deserialize(bridge_info)?;
|
|
||||||
|
|
||||||
if a.chain == CHAIN_ID_SOLANA {
|
|
||||||
return Err(Error::CannotWrapNative.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create wrapped mint
|
|
||||||
Self::create_wrapped_mint(
|
|
||||||
program_id,
|
|
||||||
accounts,
|
|
||||||
&bridge.config.token_program,
|
|
||||||
mint_info.key,
|
|
||||||
bridge_info.key,
|
|
||||||
payer_info.key,
|
|
||||||
&a,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Check and create wrapped asset meta to allow reverse resolution of info
|
|
||||||
let wrapped_meta_seeds = Bridge::derive_wrapped_meta_seeds(bridge_info.key, mint_info.key);
|
|
||||||
Bridge::check_and_create_account::<WrappedAssetMeta>(
|
|
||||||
program_id,
|
|
||||||
accounts,
|
|
||||||
wrapped_meta_info.key,
|
|
||||||
payer_info.key,
|
|
||||||
program_id,
|
|
||||||
&wrapped_meta_seeds,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut wrapped_meta_data = wrapped_meta_info.data.borrow_mut();
|
|
||||||
let wrapped_meta: &mut WrappedAssetMeta = Bridge::unpack_unchecked(&mut wrapped_meta_data)?;
|
|
||||||
|
|
||||||
wrapped_meta.is_initialized = true;
|
|
||||||
wrapped_meta.address = a.address;
|
|
||||||
wrapped_meta.chain = a.chain;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -292,6 +241,7 @@ impl Bridge {
|
||||||
let custody_info = next_account_info(account_info_iter)?;
|
let custody_info = next_account_info(account_info_iter)?;
|
||||||
|
|
||||||
let sender = Bridge::token_account_deserialize(sender_account_info)?;
|
let sender = Bridge::token_account_deserialize(sender_account_info)?;
|
||||||
|
let mint = Bridge::mint_deserialize(mint_info)?;
|
||||||
let bridge = Bridge::bridge_deserialize(bridge_info)?;
|
let bridge = Bridge::bridge_deserialize(bridge_info)?;
|
||||||
|
|
||||||
// Does the token belong to the mint
|
// Does the token belong to the mint
|
||||||
|
@ -374,6 +324,7 @@ impl Bridge {
|
||||||
transfer.asset = AssetMeta {
|
transfer.asset = AssetMeta {
|
||||||
chain: CHAIN_ID_SOLANA,
|
chain: CHAIN_ID_SOLANA,
|
||||||
address: mint_info.key.to_bytes(),
|
address: mint_info.key.to_bytes(),
|
||||||
|
decimals: mint.decimals,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -589,15 +540,52 @@ impl Bridge {
|
||||||
b.amount,
|
b.amount,
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
// Foreign chain asset, mint wrapped asset
|
// Create wrapped asset if it does not exist
|
||||||
let expected_mint_address = Bridge::derive_wrapped_asset_id(
|
if mint_info.data_is_empty() {
|
||||||
program_id,
|
let meta_info = next_account_info(account_info_iter)?;
|
||||||
bridge_info.key,
|
|
||||||
b.asset.chain,
|
// Foreign chain asset, mint wrapped asset
|
||||||
b.asset.address,
|
let expected_mint_address = Bridge::derive_wrapped_asset_id(
|
||||||
)?;
|
program_id,
|
||||||
if expected_mint_address != *mint_info.key {
|
bridge_info.key,
|
||||||
return Err(Error::InvalidDerivedAccount.into());
|
b.asset.chain,
|
||||||
|
b.asset.address,
|
||||||
|
)?;
|
||||||
|
if expected_mint_address != *mint_info.key {
|
||||||
|
return Err(Error::InvalidDerivedAccount.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create wrapped mint
|
||||||
|
Self::create_wrapped_mint(
|
||||||
|
program_id,
|
||||||
|
accounts,
|
||||||
|
&bridge.config.token_program,
|
||||||
|
mint_info.key,
|
||||||
|
bridge_info.key,
|
||||||
|
payer_info.key,
|
||||||
|
&b.asset,
|
||||||
|
b.asset.decimals,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Check and create wrapped asset meta to allow reverse resolution of info
|
||||||
|
let wrapped_meta_seeds =
|
||||||
|
Bridge::derive_wrapped_meta_seeds(bridge_info.key, mint_info.key);
|
||||||
|
Bridge::check_and_create_account::<WrappedAssetMeta>(
|
||||||
|
program_id,
|
||||||
|
accounts,
|
||||||
|
meta_info.key,
|
||||||
|
payer_info.key,
|
||||||
|
program_id,
|
||||||
|
&wrapped_meta_seeds,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut wrapped_meta_data = meta_info.data.borrow_mut();
|
||||||
|
let wrapped_meta: &mut WrappedAssetMeta =
|
||||||
|
Bridge::unpack_unchecked(&mut wrapped_meta_data)?;
|
||||||
|
|
||||||
|
wrapped_meta.is_initialized = true;
|
||||||
|
wrapped_meta.address = b.asset.address;
|
||||||
|
wrapped_meta.chain = b.asset.chain;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This automatically asserts that the mint was created by this account by using
|
// This automatically asserts that the mint was created by this account by using
|
||||||
|
@ -624,6 +612,7 @@ impl Bridge {
|
||||||
b: &BodyTransfer,
|
b: &BodyTransfer,
|
||||||
vaa_data: VAAData,
|
vaa_data: VAAData,
|
||||||
) -> ProgramResult {
|
) -> ProgramResult {
|
||||||
|
info!("posting VAA");
|
||||||
let proposal_info = next_account_info(account_info_iter)?;
|
let proposal_info = next_account_info(account_info_iter)?;
|
||||||
|
|
||||||
// Check whether the proposal was derived correctly
|
// Check whether the proposal was derived correctly
|
||||||
|
@ -784,6 +773,7 @@ impl Bridge {
|
||||||
bridge: &Pubkey,
|
bridge: &Pubkey,
|
||||||
payer: &Pubkey,
|
payer: &Pubkey,
|
||||||
asset: &AssetMeta,
|
asset: &AssetMeta,
|
||||||
|
decimals: u8,
|
||||||
) -> Result<(), ProgramError> {
|
) -> Result<(), ProgramError> {
|
||||||
Self::check_and_create_account::<Mint>(
|
Self::check_and_create_account::<Mint>(
|
||||||
program_id,
|
program_id,
|
||||||
|
@ -799,7 +789,7 @@ impl Bridge {
|
||||||
None,
|
None,
|
||||||
Some(&Self::derive_bridge_id(program_id)?),
|
Some(&Self::derive_bridge_id(program_id)?),
|
||||||
0,
|
0,
|
||||||
18,
|
decimals,
|
||||||
)?;
|
)?;
|
||||||
invoke_signed(&ix, accounts, &[])
|
invoke_signed(&ix, accounts, &[])
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,6 +136,9 @@ pub struct AssetMeta {
|
||||||
|
|
||||||
/// Chain of the token
|
/// Chain of the token
|
||||||
pub chain: u8,
|
pub chain: u8,
|
||||||
|
|
||||||
|
/// Number of decimals of the token
|
||||||
|
pub decimals: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Config for a bridge.
|
/// Config for a bridge.
|
||||||
|
|
|
@ -262,6 +262,7 @@ impl BodyTransfer {
|
||||||
let token_chain = data.read_u8()?;
|
let token_chain = data.read_u8()?;
|
||||||
let mut token_address: ForeignAddress = ForeignAddress::default();
|
let mut token_address: ForeignAddress = ForeignAddress::default();
|
||||||
data.read_exact(&mut token_address)?;
|
data.read_exact(&mut token_address)?;
|
||||||
|
let token_decimals = data.read_u8()?;
|
||||||
|
|
||||||
let mut am_data: [u8; 32] = [0; 32];
|
let mut am_data: [u8; 32] = [0; 32];
|
||||||
data.read_exact(&mut am_data)?;
|
data.read_exact(&mut am_data)?;
|
||||||
|
@ -276,6 +277,7 @@ impl BodyTransfer {
|
||||||
asset: AssetMeta {
|
asset: AssetMeta {
|
||||||
address: token_address,
|
address: token_address,
|
||||||
chain: token_chain,
|
chain: token_chain,
|
||||||
|
decimals: token_decimals,
|
||||||
},
|
},
|
||||||
amount,
|
amount,
|
||||||
})
|
})
|
||||||
|
@ -290,6 +292,7 @@ impl BodyTransfer {
|
||||||
v.write(&self.target_address)?;
|
v.write(&self.target_address)?;
|
||||||
v.write_u8(self.asset.chain)?;
|
v.write_u8(self.asset.chain)?;
|
||||||
v.write(&self.asset.address)?;
|
v.write(&self.asset.address)?;
|
||||||
|
v.write_u8(self.asset.decimals)?;
|
||||||
|
|
||||||
let mut am_data: [u8; 32] = [0; 32];
|
let mut am_data: [u8; 32] = [0; 32];
|
||||||
self.amount.to_big_endian(&mut am_data);
|
self.amount.to_big_endian(&mut am_data);
|
||||||
|
@ -328,6 +331,7 @@ mod tests {
|
||||||
asset: AssetMeta {
|
asset: AssetMeta {
|
||||||
address: [2; 32],
|
address: [2; 32],
|
||||||
chain: 8,
|
chain: 8,
|
||||||
|
decimals: 9,
|
||||||
},
|
},
|
||||||
amount: U256::from(3),
|
amount: U256::from(3),
|
||||||
})),
|
})),
|
||||||
|
@ -431,11 +435,12 @@ mod tests {
|
||||||
158, 135, 169, 32, 6, 88, 217, 196, 14, 153, 136,
|
158, 135, 169, 32, 6, 88, 217, 196, 14, 153, 136,
|
||||||
],
|
],
|
||||||
chain: 1,
|
chain: 1,
|
||||||
|
decimals: 8,
|
||||||
},
|
},
|
||||||
amount: U256::from_dec_str("5000000000000000000").unwrap(),
|
amount: U256::from_dec_str("5000000000000000000").unwrap(),
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
let data = hex::decode("0100000000010092737a1504f3b3df8c93cb85c64a4860bb270e26026b6e37f095356a406f6af439c6b2e9775fa1c6669525f06edab033ba5d447308f4e3bdb33c0f361dc32ec3015f37000810000000350102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e99880000000000000000000000000000000000000000000000004563918244f40000").unwrap();
|
let data = hex::decode("0100000000010092737a1504f3b3df8c93cb85c64a4860bb270e26026b6e37f095356a406f6af439c6b2e9775fa1c6669525f06edab033ba5d447308f4e3bdb33c0f361dc32ec3015f37000810000000350102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1010000000000000000000000000347ef34687bdc9f189e87a9200658d9c40e9988080000000000000000000000000000000000000000000000004563918244f40000").unwrap();
|
||||||
let parsed_vaa = VAA::deserialize(data.as_slice()).unwrap();
|
let parsed_vaa = VAA::deserialize(data.as_slice()).unwrap();
|
||||||
assert_eq!(vaa, parsed_vaa);
|
assert_eq!(vaa, parsed_vaa);
|
||||||
|
|
||||||
|
|
|
@ -112,11 +112,13 @@ fn command_lock_tokens(
|
||||||
AssetMeta {
|
AssetMeta {
|
||||||
address: wrapped_meta.address,
|
address: wrapped_meta.address,
|
||||||
chain: wrapped_meta.chain,
|
chain: wrapped_meta.chain,
|
||||||
|
decimals: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => AssetMeta {
|
Err(e) => AssetMeta {
|
||||||
address: token.to_bytes(),
|
address: token.to_bytes(),
|
||||||
chain: CHAIN_ID_SOLANA,
|
chain: CHAIN_ID_SOLANA,
|
||||||
|
decimals: 0,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -161,30 +163,6 @@ fn command_lock_tokens(
|
||||||
Ok(Some(transaction))
|
Ok(Some(transaction))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn command_create_wrapped_asset(
|
|
||||||
config: &Config,
|
|
||||||
bridge: &Pubkey,
|
|
||||||
meta: AssetMeta,
|
|
||||||
) -> CommmandResult {
|
|
||||||
println!("Creating wrapped asset");
|
|
||||||
|
|
||||||
let minimum_balance_for_rent_exemption = config
|
|
||||||
.rpc_client
|
|
||||||
.get_minimum_balance_for_rent_exemption(size_of::<Mint>())?;
|
|
||||||
|
|
||||||
let ix = create_wrapped(bridge, &config.owner.pubkey(), meta)?;
|
|
||||||
|
|
||||||
let mut transaction = Transaction::new_with_payer(&[ix], Some(&config.fee_payer.pubkey()));
|
|
||||||
|
|
||||||
let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?;
|
|
||||||
check_fee_payer_balance(
|
|
||||||
config,
|
|
||||||
minimum_balance_for_rent_exemption + fee_calculator.calculate_fee(&transaction.message()),
|
|
||||||
)?;
|
|
||||||
transaction.sign(&[&config.fee_payer, &config.owner], recent_blockhash);
|
|
||||||
Ok(Some(transaction))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn command_submit_vaa(config: &Config, bridge: &Pubkey, vaa: &[u8]) -> CommmandResult {
|
fn command_submit_vaa(config: &Config, bridge: &Pubkey, vaa: &[u8]) -> CommmandResult {
|
||||||
println!("Submitting VAA");
|
println!("Submitting VAA");
|
||||||
|
|
||||||
|
@ -976,40 +954,6 @@ fn main() {
|
||||||
.help("The vaa to be posted"),
|
.help("The vaa to be posted"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.subcommand(
|
|
||||||
SubCommand::with_name("create-wrapped")
|
|
||||||
.about("Create new wrapped asset and token account")
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("bridge")
|
|
||||||
.long("bridge")
|
|
||||||
.value_name("BRIDGE_KEY")
|
|
||||||
.validator(is_pubkey_or_keypair)
|
|
||||||
.takes_value(true)
|
|
||||||
.index(1)
|
|
||||||
.required(true)
|
|
||||||
.help(
|
|
||||||
"Specify the bridge program public key"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("chain")
|
|
||||||
.validator(is_u8)
|
|
||||||
.value_name("CHAIN")
|
|
||||||
.takes_value(true)
|
|
||||||
.index(2)
|
|
||||||
.required(true)
|
|
||||||
.help("Chain ID of the asset"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("token")
|
|
||||||
.validator(is_hex)
|
|
||||||
.value_name("TOKEN_ADDRESS")
|
|
||||||
.takes_value(true)
|
|
||||||
.index(3)
|
|
||||||
.required(true)
|
|
||||||
.help("Token address of the asset"),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("wrapped-address")
|
SubCommand::with_name("wrapped-address")
|
||||||
.about("Derive wrapped asset address")
|
.about("Derive wrapped asset address")
|
||||||
|
@ -1170,24 +1114,6 @@ fn main() {
|
||||||
let vaa = hex::decode(vaa_string).unwrap();
|
let vaa = hex::decode(vaa_string).unwrap();
|
||||||
command_submit_vaa(&config, &bridge, vaa.as_slice())
|
command_submit_vaa(&config, &bridge, vaa.as_slice())
|
||||||
}
|
}
|
||||||
("create-wrapped", Some(arg_matches)) => {
|
|
||||||
let bridge = pubkey_of(arg_matches, "bridge").unwrap();
|
|
||||||
let chain = value_t_or_exit!(arg_matches, "chain", u8);
|
|
||||||
let addr_string: String = value_of(arg_matches, "token").unwrap();
|
|
||||||
let addr_data = hex::decode(addr_string).unwrap();
|
|
||||||
|
|
||||||
let mut token_addr = [0u8; 32];
|
|
||||||
token_addr.copy_from_slice(addr_data.as_slice());
|
|
||||||
|
|
||||||
command_create_wrapped_asset(
|
|
||||||
&config,
|
|
||||||
&bridge,
|
|
||||||
AssetMeta {
|
|
||||||
chain,
|
|
||||||
address: token_addr,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
("wrapped-address", Some(arg_matches)) => {
|
("wrapped-address", Some(arg_matches)) => {
|
||||||
let bridge = pubkey_of(arg_matches, "bridge").unwrap();
|
let bridge = pubkey_of(arg_matches, "bridge").unwrap();
|
||||||
let chain = value_t_or_exit!(arg_matches, "chain", u8);
|
let chain = value_t_or_exit!(arg_matches, "chain", u8);
|
||||||
|
|
|
@ -734,3 +734,23 @@ Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
||||||
echo "${genesis_args[@]}" > spl-genesis-args.sh
|
echo "${genesis_args[@]}" > spl-genesis-args.sh
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
Index: account-decoder/src/parse_token.rs
|
||||||
|
IDEA additional info:
|
||||||
|
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
||||||
|
<+>UTF-8
|
||||||
|
===================================================================
|
||||||
|
--- account-decoder/src/parse_token.rs (revision 97144cdb8e9cb4d83943b0b5898d08f57844a4dd)
|
||||||
|
+++ account-decoder/src/parse_token.rs (date 1598606089966)
|
||||||
|
@@ -30,11 +30,7 @@
|
||||||
|
if data.len() == size_of::<Account>() {
|
||||||
|
let account: Account = *unpack(&mut data)
|
||||||
|
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?;
|
||||||
|
- let decimals = mint_decimals.ok_or_else(|| {
|
||||||
|
- ParseAccountError::AdditionalDataMissing(
|
||||||
|
- "no mint_decimals provided to parse spl-token account".to_string(),
|
||||||
|
- )
|
||||||
|
- })?;
|
||||||
|
+ let decimals = mint_decimals.or(Some(0)).unwrap();
|
||||||
|
Ok(TokenAccountType::Account(UiTokenAccount {
|
||||||
|
mint: account.mint.to_string(),
|
||||||
|
owner: account.owner.to_string(),
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -2006,6 +2006,14 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/bs58": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/bs58/-/bs58-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-yfAgiWgVLjFCmRv8zAcOIHywYATEwiTVccTLnRp6UxTNavT55M9d/uhK3T03St/+8/z/wW+CRjGKUNmEqoHHCA==",
|
||||||
|
"requires": {
|
||||||
|
"base-x": "^3.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/color-name": {
|
"@types/color-name": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
"@types/react": "^16.9.0",
|
"@types/react": "^16.9.0",
|
||||||
"@types/react-dom": "^16.9.0",
|
"@types/react-dom": "^16.9.0",
|
||||||
"@types/react-router-dom": "^5.1.5",
|
"@types/react-router-dom": "^5.1.5",
|
||||||
|
"@types/bs58": "^4.0.1",
|
||||||
"antd": "^4.4.1",
|
"antd": "^4.4.1",
|
||||||
"buffer": "^5.6.0",
|
"buffer": "^5.6.0",
|
||||||
"buffer-layout": "^1.2.0",
|
"buffer-layout": "^1.2.0",
|
||||||
|
@ -23,7 +24,8 @@
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-scripts": "3.4.1",
|
"react-scripts": "3.4.1",
|
||||||
"typescript": "~3.7.2",
|
"typescript": "~3.7.2",
|
||||||
"web3": "^1.2.9"
|
"web3": "^1.2.9",
|
||||||
|
"bs58": "^4.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"npm": "^6.14.6",
|
"npm": "^6.14.6",
|
||||||
|
|
|
@ -3,6 +3,9 @@ import {BalanceInfo, SolanaTokenContext} from "../providers/SolanaTokenContext";
|
||||||
import {Table} from "antd";
|
import {Table} from "antd";
|
||||||
import {CHAIN_ID_SOLANA} from "../utils/bridge";
|
import {CHAIN_ID_SOLANA} from "../utils/bridge";
|
||||||
import {BigNumber} from "ethers/utils";
|
import {BigNumber} from "ethers/utils";
|
||||||
|
import {PublicKey} from "@solana/web3.js";
|
||||||
|
import {deriveERC20Address} from "../utils/helpers";
|
||||||
|
|
||||||
|
|
||||||
function SplBalances() {
|
function SplBalances() {
|
||||||
let t = useContext(SolanaTokenContext);
|
let t = useContext(SolanaTokenContext);
|
||||||
|
@ -27,7 +30,7 @@ function SplBalances() {
|
||||||
title: 'Wrapped',
|
title: 'Wrapped',
|
||||||
key: 'wrapped',
|
key: 'wrapped',
|
||||||
render: (n: any, v: BalanceInfo) => {
|
render: (n: any, v: BalanceInfo) => {
|
||||||
return v.assetMeta.chain != CHAIN_ID_SOLANA ? `Wrapped (${v.assetMeta.chain} - 0x${v.assetMeta.address.slice(12).toString("hex")})` : "Native"
|
return v.assetMeta.chain != CHAIN_ID_SOLANA ? `Wrapped (${v.assetMeta.chain == 2 ? "ETH" : "SOL"} - 0x${v.assetMeta.address.slice(12).toString("hex")})` : `Native (0x${deriveERC20Address(new PublicKey(v.mint))})`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
import React, {useContext, useEffect, useState} from "react"
|
||||||
|
import {SolanaTokenContext} from "../providers/SolanaTokenContext";
|
||||||
|
import {Table} from "antd";
|
||||||
|
import {Lockup} from "../utils/bridge";
|
||||||
|
import {BridgeContext} from "../providers/BridgeContext";
|
||||||
|
import {SlotContext} from "../providers/SlotContext";
|
||||||
|
import {ethers} from "ethers";
|
||||||
|
import {WormholeFactory} from "../contracts/WormholeFactory";
|
||||||
|
import {BRIDGE_ADDRESS} from "../config";
|
||||||
|
import {keccak256} from "ethers/utils";
|
||||||
|
// @ts-ignore
|
||||||
|
const provider = new ethers.providers.Web3Provider(window.ethereum);
|
||||||
|
|
||||||
|
function TransferProposals() {
|
||||||
|
let s = useContext(SlotContext);
|
||||||
|
let t = useContext(SolanaTokenContext);
|
||||||
|
let tokens = useContext(SolanaTokenContext);
|
||||||
|
let b = useContext(BridgeContext);
|
||||||
|
|
||||||
|
let [lockups, setLockups] = useState<Lockup[]>([])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (s % 10 !== 0) return;
|
||||||
|
|
||||||
|
let updateLockups = async () => {
|
||||||
|
let lockups = [];
|
||||||
|
for (let account of tokens.balances) {
|
||||||
|
let accLockups = await b.fetchTransferProposals(account.account)
|
||||||
|
lockups.push(...accLockups)
|
||||||
|
}
|
||||||
|
|
||||||
|
let wormhole = WormholeFactory.connect(BRIDGE_ADDRESS, provider);
|
||||||
|
for (let lockup of lockups) {
|
||||||
|
console.log(lockup)
|
||||||
|
|
||||||
|
if (lockup.vaaTime === undefined || lockup.vaaTime === 0) continue;
|
||||||
|
|
||||||
|
let signingData = lockup.vaa.slice(lockup.vaa[5] * 66 + 6)
|
||||||
|
let hash = keccak256(signingData)
|
||||||
|
|
||||||
|
let status = await wormhole.consumedVAAs(hash)
|
||||||
|
lockup.initialized = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLockups(lockups);
|
||||||
|
}
|
||||||
|
updateLockups()
|
||||||
|
}, [s])
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: 'SourceAccount',
|
||||||
|
key: 'source',
|
||||||
|
render: (n: any, v: Lockup) => v.sourceAddress.toString()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Mint',
|
||||||
|
key: 'assetAddress',
|
||||||
|
render: (n: any, v: Lockup) => v.assetAddress.toString()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Amount',
|
||||||
|
key: 'amount',
|
||||||
|
render: (n: any, v: Lockup) => v.amount.toString()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Status',
|
||||||
|
key: 'status',
|
||||||
|
render: (n: any, v: Lockup) => {
|
||||||
|
return (<>Pending {v.initialized}</>)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (<>
|
||||||
|
<h3>Pending transfers</h3>
|
||||||
|
<Table dataSource={lockups} columns={columns} pagination={false} scroll={{y: 400}}/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TransferProposals
|
|
@ -1,6 +1,8 @@
|
||||||
import {PublicKey} from "@solana/web3.js";
|
import {PublicKey} from "@solana/web3.js";
|
||||||
|
|
||||||
const BRIDGE_ADDRESS = "0x5b1869D9A4C187F2EAa108f3062412ecf0526b24";
|
const BRIDGE_ADDRESS = "0x5b1869D9A4C187F2EAa108f3062412ecf0526b24";
|
||||||
|
const WRAPPED_MASTER = "e78A0F7E598Cc8b0Bb87894B0F60dD2a88d6a8Ab"
|
||||||
|
|
||||||
|
|
||||||
const SOLANA_BRIDGE_PROGRAM = new PublicKey("Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o");
|
const SOLANA_BRIDGE_PROGRAM = new PublicKey("Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o");
|
||||||
const TOKEN_PROGRAM = new PublicKey("TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o");
|
const TOKEN_PROGRAM = new PublicKey("TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o");
|
||||||
|
@ -9,5 +11,6 @@ const TOKEN_PROGRAM = new PublicKey("TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o
|
||||||
export {
|
export {
|
||||||
BRIDGE_ADDRESS,
|
BRIDGE_ADDRESS,
|
||||||
TOKEN_PROGRAM,
|
TOKEN_PROGRAM,
|
||||||
|
WRAPPED_MASTER,
|
||||||
SOLANA_BRIDGE_PROGRAM
|
SOLANA_BRIDGE_PROGRAM
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,10 @@ import {
|
||||||
|
|
||||||
interface WormholeInterface extends Interface {
|
interface WormholeInterface extends Interface {
|
||||||
functions: {
|
functions: {
|
||||||
|
consumedVAAs: TypedFunctionDescription<{ encode([]: [Arrayish]): string }>;
|
||||||
|
|
||||||
|
guardian_set_expirity: TypedFunctionDescription<{ encode([]: []): string }>;
|
||||||
|
|
||||||
guardian_set_index: TypedFunctionDescription<{ encode([]: []): string }>;
|
guardian_set_index: TypedFunctionDescription<{ encode([]: []): string }>;
|
||||||
|
|
||||||
guardian_sets: TypedFunctionDescription<{
|
guardian_sets: TypedFunctionDescription<{
|
||||||
|
@ -20,8 +24,6 @@ interface WormholeInterface extends Interface {
|
||||||
|
|
||||||
isWrappedAsset: TypedFunctionDescription<{ encode([]: [string]): string }>;
|
isWrappedAsset: TypedFunctionDescription<{ encode([]: [string]): string }>;
|
||||||
|
|
||||||
vaa_expiry: TypedFunctionDescription<{ encode([]: []): string }>;
|
|
||||||
|
|
||||||
wrappedAssetMaster: TypedFunctionDescription<{ encode([]: []): string }>;
|
wrappedAssetMaster: TypedFunctionDescription<{ encode([]: []): string }>;
|
||||||
|
|
||||||
wrappedAssets: TypedFunctionDescription<{ encode([]: [Arrayish]): string }>;
|
wrappedAssets: TypedFunctionDescription<{ encode([]: [Arrayish]): string }>;
|
||||||
|
@ -63,12 +65,14 @@ interface WormholeInterface extends Interface {
|
||||||
encodeTopics([
|
encodeTopics([
|
||||||
target_chain,
|
target_chain,
|
||||||
token_chain,
|
token_chain,
|
||||||
|
token_decimals,
|
||||||
token,
|
token,
|
||||||
sender,
|
sender,
|
||||||
recipient,
|
recipient,
|
||||||
amount,
|
amount,
|
||||||
nonce
|
nonce
|
||||||
]: [
|
]: [
|
||||||
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Arrayish | null,
|
Arrayish | null,
|
||||||
|
@ -95,6 +99,22 @@ export class Wormhole extends Contract {
|
||||||
interface: WormholeInterface;
|
interface: WormholeInterface;
|
||||||
|
|
||||||
functions: {
|
functions: {
|
||||||
|
consumedVAAs(
|
||||||
|
arg0: Arrayish,
|
||||||
|
overrides?: TransactionOverrides
|
||||||
|
): Promise<boolean>;
|
||||||
|
|
||||||
|
"consumedVAAs(bytes32)"(
|
||||||
|
arg0: Arrayish,
|
||||||
|
overrides?: TransactionOverrides
|
||||||
|
): Promise<boolean>;
|
||||||
|
|
||||||
|
guardian_set_expirity(overrides?: TransactionOverrides): Promise<number>;
|
||||||
|
|
||||||
|
"guardian_set_expirity()"(
|
||||||
|
overrides?: TransactionOverrides
|
||||||
|
): Promise<number>;
|
||||||
|
|
||||||
guardian_set_index(overrides?: TransactionOverrides): Promise<number>;
|
guardian_set_index(overrides?: TransactionOverrides): Promise<number>;
|
||||||
|
|
||||||
"guardian_set_index()"(overrides?: TransactionOverrides): Promise<number>;
|
"guardian_set_index()"(overrides?: TransactionOverrides): Promise<number>;
|
||||||
|
@ -119,10 +139,6 @@ export class Wormhole extends Contract {
|
||||||
overrides?: TransactionOverrides
|
overrides?: TransactionOverrides
|
||||||
): Promise<boolean>;
|
): Promise<boolean>;
|
||||||
|
|
||||||
vaa_expiry(overrides?: TransactionOverrides): Promise<number>;
|
|
||||||
|
|
||||||
"vaa_expiry()"(overrides?: TransactionOverrides): Promise<number>;
|
|
||||||
|
|
||||||
wrappedAssetMaster(overrides?: TransactionOverrides): Promise<string>;
|
wrappedAssetMaster(overrides?: TransactionOverrides): Promise<string>;
|
||||||
|
|
||||||
"wrappedAssetMaster()"(overrides?: TransactionOverrides): Promise<string>;
|
"wrappedAssetMaster()"(overrides?: TransactionOverrides): Promise<string>;
|
||||||
|
@ -200,6 +216,20 @@ export class Wormhole extends Contract {
|
||||||
): Promise<ContractTransaction>;
|
): Promise<ContractTransaction>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
consumedVAAs(
|
||||||
|
arg0: Arrayish,
|
||||||
|
overrides?: TransactionOverrides
|
||||||
|
): Promise<boolean>;
|
||||||
|
|
||||||
|
"consumedVAAs(bytes32)"(
|
||||||
|
arg0: Arrayish,
|
||||||
|
overrides?: TransactionOverrides
|
||||||
|
): Promise<boolean>;
|
||||||
|
|
||||||
|
guardian_set_expirity(overrides?: TransactionOverrides): Promise<number>;
|
||||||
|
|
||||||
|
"guardian_set_expirity()"(overrides?: TransactionOverrides): Promise<number>;
|
||||||
|
|
||||||
guardian_set_index(overrides?: TransactionOverrides): Promise<number>;
|
guardian_set_index(overrides?: TransactionOverrides): Promise<number>;
|
||||||
|
|
||||||
"guardian_set_index()"(overrides?: TransactionOverrides): Promise<number>;
|
"guardian_set_index()"(overrides?: TransactionOverrides): Promise<number>;
|
||||||
|
@ -224,10 +254,6 @@ export class Wormhole extends Contract {
|
||||||
overrides?: TransactionOverrides
|
overrides?: TransactionOverrides
|
||||||
): Promise<boolean>;
|
): Promise<boolean>;
|
||||||
|
|
||||||
vaa_expiry(overrides?: TransactionOverrides): Promise<number>;
|
|
||||||
|
|
||||||
"vaa_expiry()"(overrides?: TransactionOverrides): Promise<number>;
|
|
||||||
|
|
||||||
wrappedAssetMaster(overrides?: TransactionOverrides): Promise<string>;
|
wrappedAssetMaster(overrides?: TransactionOverrides): Promise<string>;
|
||||||
|
|
||||||
"wrappedAssetMaster()"(overrides?: TransactionOverrides): Promise<string>;
|
"wrappedAssetMaster()"(overrides?: TransactionOverrides): Promise<string>;
|
||||||
|
@ -313,6 +339,7 @@ export class Wormhole extends Contract {
|
||||||
LogTokensLocked(
|
LogTokensLocked(
|
||||||
target_chain: null,
|
target_chain: null,
|
||||||
token_chain: null,
|
token_chain: null,
|
||||||
|
token_decimals: null,
|
||||||
token: Arrayish | null,
|
token: Arrayish | null,
|
||||||
sender: Arrayish | null,
|
sender: Arrayish | null,
|
||||||
recipient: null,
|
recipient: null,
|
||||||
|
@ -322,6 +349,22 @@ export class Wormhole extends Contract {
|
||||||
};
|
};
|
||||||
|
|
||||||
estimate: {
|
estimate: {
|
||||||
|
consumedVAAs(
|
||||||
|
arg0: Arrayish,
|
||||||
|
overrides?: TransactionOverrides
|
||||||
|
): Promise<BigNumber>;
|
||||||
|
|
||||||
|
"consumedVAAs(bytes32)"(
|
||||||
|
arg0: Arrayish,
|
||||||
|
overrides?: TransactionOverrides
|
||||||
|
): Promise<BigNumber>;
|
||||||
|
|
||||||
|
guardian_set_expirity(overrides?: TransactionOverrides): Promise<BigNumber>;
|
||||||
|
|
||||||
|
"guardian_set_expirity()"(
|
||||||
|
overrides?: TransactionOverrides
|
||||||
|
): Promise<BigNumber>;
|
||||||
|
|
||||||
guardian_set_index(overrides?: TransactionOverrides): Promise<BigNumber>;
|
guardian_set_index(overrides?: TransactionOverrides): Promise<BigNumber>;
|
||||||
|
|
||||||
"guardian_set_index()"(
|
"guardian_set_index()"(
|
||||||
|
@ -348,10 +391,6 @@ export class Wormhole extends Contract {
|
||||||
overrides?: TransactionOverrides
|
overrides?: TransactionOverrides
|
||||||
): Promise<BigNumber>;
|
): Promise<BigNumber>;
|
||||||
|
|
||||||
vaa_expiry(overrides?: TransactionOverrides): Promise<BigNumber>;
|
|
||||||
|
|
||||||
"vaa_expiry()"(overrides?: TransactionOverrides): Promise<BigNumber>;
|
|
||||||
|
|
||||||
wrappedAssetMaster(overrides?: TransactionOverrides): Promise<BigNumber>;
|
wrappedAssetMaster(overrides?: TransactionOverrides): Promise<BigNumber>;
|
||||||
|
|
||||||
"wrappedAssetMaster()"(
|
"wrappedAssetMaster()"(
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -15,6 +15,7 @@ import {AssetMeta, SolanaBridge} from "../utils/bridge";
|
||||||
import KeyContext from "../providers/KeyContext";
|
import KeyContext from "../providers/KeyContext";
|
||||||
import {FormInstance} from "antd/lib/form";
|
import {FormInstance} from "antd/lib/form";
|
||||||
import SplBalances from "../components/SplBalances";
|
import SplBalances from "../components/SplBalances";
|
||||||
|
import TransferProposals from "../components/TransferProposals";
|
||||||
|
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -57,11 +58,6 @@ async function approveAssets(asset: string,
|
||||||
async function createWrapped(c: Connection, b: SolanaBridge, key: Account, meta: AssetMeta, mint: PublicKey) {
|
async function createWrapped(c: Connection, b: SolanaBridge, key: Account, meta: AssetMeta, mint: PublicKey) {
|
||||||
try {
|
try {
|
||||||
let tx = new Transaction();
|
let tx = new Transaction();
|
||||||
let mintAccount = await c.getAccountInfo(mint);
|
|
||||||
if (!mintAccount) {
|
|
||||||
let ix = await b.createWrappedAssetInstruction(key.publicKey, meta);
|
|
||||||
tx.add(ix)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let [ix_account, newSigner] = await b.createWrappedAssetAndAccountInstructions(key.publicKey, mint);
|
let [ix_account, newSigner] = await b.createWrappedAssetAndAccountInstructions(key.publicKey, mint);
|
||||||
|
@ -279,6 +275,11 @@ function Transfer() {
|
||||||
<SplBalances/>
|
<SplBalances/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col>
|
||||||
|
<TransferProposals/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ function TransferSolana() {
|
||||||
{
|
{
|
||||||
chain: coinInfo.chainID,
|
chain: coinInfo.chainID,
|
||||||
address: coinInfo.wrappedAddress
|
address: coinInfo.wrappedAddress
|
||||||
}, 2);
|
}, Math.random()*100000);
|
||||||
let ix = spl.Token.createApproveInstruction(TOKEN_PROGRAM, fromAccount, await bridge.getConfigKey(), k.publicKey, [], transferAmount.toNumber())
|
let ix = spl.Token.createApproveInstruction(TOKEN_PROGRAM, fromAccount, await bridge.getConfigKey(), k.publicKey, [], transferAmount.toNumber())
|
||||||
|
|
||||||
let recentHash = await c.getRecentBlockhash();
|
let recentHash = await c.getRecentBlockhash();
|
||||||
|
|
|
@ -3,5 +3,5 @@ import * as solanaWeb3 from '@solana/web3.js';
|
||||||
import {Account} from "@solana/web3.js";
|
import {Account} from "@solana/web3.js";
|
||||||
|
|
||||||
|
|
||||||
const KeyContext = React.createContext<Account>(new Account([97,215,234,123,197,228,56,3,210,182,139,102,127,246,235,213,211,40,93,149,16,226,130,1,29,196,87,105,185,115,179,53,123,232,195,48,5,229,144,176,217,8,1,27,185,162,160,157,137,210,99,173,135,148,20,232,241,43,238,229,1,61,122,183]));
|
const KeyContext = React.createContext<Account>(new Account([14,173,153,4,176,224,201,111,32,237,183,185,159,247,22,161,89,84,215,209,212,137,10,92,157,49,29,192,101,164,152,70,87,65,8,174,214,157,175,126,98,90,54,24,100,177,247,77,19,112,47,44,165,109,233,102,14,86,109,29,134,145,132,141]));
|
||||||
export default KeyContext
|
export default KeyContext
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {TOKEN_PROGRAM} from "../config";
|
||||||
import {BridgeContext} from "./BridgeContext";
|
import {BridgeContext} from "./BridgeContext";
|
||||||
import {message} from "antd";
|
import {message} from "antd";
|
||||||
import {AssetMeta} from "../utils/bridge";
|
import {AssetMeta} from "../utils/bridge";
|
||||||
|
import {Buffer} from "buffer";
|
||||||
|
|
||||||
export interface BalanceInfo {
|
export interface BalanceInfo {
|
||||||
mint: string,
|
mint: string,
|
||||||
|
|
|
@ -6,12 +6,26 @@ import assert from "assert";
|
||||||
import * as BufferLayout from 'buffer-layout'
|
import * as BufferLayout from 'buffer-layout'
|
||||||
import {Token} from "@solana/spl-token";
|
import {Token} from "@solana/spl-token";
|
||||||
import {TOKEN_PROGRAM} from "../config";
|
import {TOKEN_PROGRAM} from "../config";
|
||||||
|
import * as bs58 from "bs58";
|
||||||
|
|
||||||
export interface AssetMeta {
|
export interface AssetMeta {
|
||||||
chain: number,
|
chain: number,
|
||||||
address: Buffer
|
address: Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Lockup {
|
||||||
|
amount: BN,
|
||||||
|
toChain: number,
|
||||||
|
sourceAddress: PublicKey,
|
||||||
|
targetAddress: Uint8Array,
|
||||||
|
assetAddress: Uint8Array,
|
||||||
|
assetChain: number,
|
||||||
|
nonce: number,
|
||||||
|
vaa: Uint8Array,
|
||||||
|
vaaTime: number,
|
||||||
|
initialized: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
export const CHAIN_ID_SOLANA = 1;
|
export const CHAIN_ID_SOLANA = 1;
|
||||||
|
|
||||||
class SolanaBridge {
|
class SolanaBridge {
|
||||||
|
@ -25,51 +39,6 @@ class SolanaBridge {
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createWrappedAssetInstruction(
|
|
||||||
payer: PublicKey,
|
|
||||||
asset: AssetMeta,
|
|
||||||
): Promise<TransactionInstruction> {
|
|
||||||
const dataLayout = BufferLayout.struct([
|
|
||||||
BufferLayout.u8('instruction'),
|
|
||||||
BufferLayout.blob(32, 'address'),
|
|
||||||
BufferLayout.u8('chain'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
let configKey = await this.getConfigKey();
|
|
||||||
let seeds: Array<Buffer> = [Buffer.from("wrapped"), configKey.toBuffer(), Buffer.from([asset.chain]),
|
|
||||||
padBuffer(asset.address, 32)];
|
|
||||||
// @ts-ignore
|
|
||||||
let wrappedKey = (await solanaWeb3.PublicKey.findProgramAddress(seeds, this.programID))[0];
|
|
||||||
// @ts-ignore
|
|
||||||
let wrappedMetaKey = (await solanaWeb3.PublicKey.findProgramAddress([Buffer.from("meta"), configKey.toBuffer(), wrappedKey.toBuffer()], this.programID))[0];
|
|
||||||
|
|
||||||
const data = Buffer.alloc(dataLayout.span);
|
|
||||||
dataLayout.encode(
|
|
||||||
{
|
|
||||||
instruction: 5, // CreateWrapped instruction
|
|
||||||
address: padBuffer(asset.address, 32),
|
|
||||||
chain: asset.chain,
|
|
||||||
},
|
|
||||||
data,
|
|
||||||
);
|
|
||||||
|
|
||||||
const keys = [
|
|
||||||
{pubkey: this.programID, isSigner: false, isWritable: false},
|
|
||||||
{pubkey: solanaWeb3.SystemProgram.programId, isSigner: false, isWritable: false},
|
|
||||||
{pubkey: this.tokenProgram, isSigner: false, isWritable: false},
|
|
||||||
{pubkey: configKey, isSigner: false, isWritable: false},
|
|
||||||
{pubkey: payer, isSigner: true, isWritable: true},
|
|
||||||
{pubkey: wrappedKey, isSigner: false, isWritable: true},
|
|
||||||
{pubkey: wrappedMetaKey, isSigner: false, isWritable: true},
|
|
||||||
];
|
|
||||||
return new TransactionInstruction({
|
|
||||||
keys,
|
|
||||||
programId: this.programID,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async createLockAssetInstruction(
|
async createLockAssetInstruction(
|
||||||
payer: PublicKey,
|
payer: PublicKey,
|
||||||
tokenAccount: PublicKey,
|
tokenAccount: PublicKey,
|
||||||
|
@ -86,8 +55,9 @@ class SolanaBridge {
|
||||||
BufferLayout.u8('targetChain'),
|
BufferLayout.u8('targetChain'),
|
||||||
BufferLayout.blob(32, 'assetAddress'),
|
BufferLayout.blob(32, 'assetAddress'),
|
||||||
BufferLayout.u8('assetChain'),
|
BufferLayout.u8('assetChain'),
|
||||||
|
BufferLayout.u8('assetDecimals'),
|
||||||
BufferLayout.blob(32, 'targetAddress'),
|
BufferLayout.blob(32, 'targetAddress'),
|
||||||
BufferLayout.seq(BufferLayout.u8(), 2),
|
BufferLayout.seq(BufferLayout.u8(), 1),
|
||||||
BufferLayout.u32('nonce'),
|
BufferLayout.u32('nonce'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -111,6 +81,7 @@ class SolanaBridge {
|
||||||
targetChain: targetChain,
|
targetChain: targetChain,
|
||||||
assetAddress: padBuffer(asset.address, 32),
|
assetAddress: padBuffer(asset.address, 32),
|
||||||
assetChain: asset.chain,
|
assetChain: asset.chain,
|
||||||
|
assetDecimals: 0, // This is fetched on chain
|
||||||
targetAddress: padBuffer(targetAddress, 32),
|
targetAddress: padBuffer(targetAddress, 32),
|
||||||
nonce: nonce,
|
nonce: nonce,
|
||||||
},
|
},
|
||||||
|
@ -172,6 +143,67 @@ class SolanaBridge {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fetchAssetMeta fetches the AssetMeta for an SPL token
|
||||||
|
async fetchTransferProposals(
|
||||||
|
tokenAccount: PublicKey,
|
||||||
|
): Promise<Lockup[]> {
|
||||||
|
let accountRes = await fetch("http://localhost:8899", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"method": "getProgramAccounts",
|
||||||
|
"params": [this.programID.toString(), {
|
||||||
|
"filters": [{"dataSize": 1152}, {
|
||||||
|
"memcmp": {
|
||||||
|
"offset": 33,
|
||||||
|
"bytes": tokenAccount.toString()
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
let raw_accounts = (await accountRes.json())["result"];
|
||||||
|
|
||||||
|
const dataLayout = BufferLayout.struct([
|
||||||
|
uint256('amount'),
|
||||||
|
BufferLayout.u8('toChain'),
|
||||||
|
BufferLayout.blob(32, 'sourceAddress'),
|
||||||
|
BufferLayout.blob(32, 'targetAddress'),
|
||||||
|
BufferLayout.blob(32, 'assetAddress'),
|
||||||
|
BufferLayout.u8('assetChain'),
|
||||||
|
BufferLayout.u8('assetDecimals'),
|
||||||
|
BufferLayout.u32('nonce'),
|
||||||
|
BufferLayout.blob(1001, 'vaa'),
|
||||||
|
BufferLayout.u32('vaaTime'),
|
||||||
|
BufferLayout.u8('initialized'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let accounts: Lockup[] = [];
|
||||||
|
for (let acc of raw_accounts) {
|
||||||
|
acc = acc.account;
|
||||||
|
let parsedAccount = dataLayout.decode(bs58.decode(acc.data))
|
||||||
|
console.log(parsedAccount);
|
||||||
|
accounts.push({
|
||||||
|
amount: new BN(parsedAccount.amount, 2, "le"),
|
||||||
|
assetAddress: parsedAccount.assetAddress,
|
||||||
|
assetChain: acc.assetChain,
|
||||||
|
initialized: acc.initialized == 1,
|
||||||
|
nonce: acc.nonce,
|
||||||
|
sourceAddress: new PublicKey(parsedAccount.sourceAddress),
|
||||||
|
targetAddress: parsedAccount.targetAddress,
|
||||||
|
toChain: acc.toChain,
|
||||||
|
vaa: acc.vaa,
|
||||||
|
vaaTime: acc.vaaTime
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return accounts
|
||||||
|
}
|
||||||
|
|
||||||
AccountLayout = BufferLayout.struct([publicKey('mint'), publicKey('owner'), uint64('amount'), BufferLayout.u32('option'), publicKey('delegate'), BufferLayout.u8('is_initialized'), BufferLayout.u8('is_native'), BufferLayout.u16('padding'), uint64('delegatedAmount')]);
|
AccountLayout = BufferLayout.struct([publicKey('mint'), publicKey('owner'), uint64('amount'), BufferLayout.u32('option'), publicKey('delegate'), BufferLayout.u8('is_initialized'), BufferLayout.u8('is_native'), BufferLayout.u16('padding'), uint64('delegatedAmount')]);
|
||||||
|
|
||||||
async createWrappedAssetAndAccountInstructions(owner: PublicKey, mint: PublicKey): Promise<[TransactionInstruction[], solanaWeb3.Account]> {
|
async createWrappedAssetAndAccountInstructions(owner: PublicKey, mint: PublicKey): Promise<[TransactionInstruction[], solanaWeb3.Account]> {
|
||||||
|
@ -221,6 +253,10 @@ class SolanaBridge {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getWrappedAssetMint(asset: AssetMeta): Promise<PublicKey> {
|
async getWrappedAssetMint(asset: AssetMeta): Promise<PublicKey> {
|
||||||
|
if (asset.chain === 1) {
|
||||||
|
return new PublicKey(asset.address)
|
||||||
|
}
|
||||||
|
|
||||||
let configKey = await this.getConfigKey();
|
let configKey = await this.getConfigKey();
|
||||||
let seeds: Array<Buffer> = [Buffer.from("wrapped"), configKey.toBuffer(), Buffer.of(asset.chain),
|
let seeds: Array<Buffer> = [Buffer.from("wrapped"), configKey.toBuffer(), Buffer.of(asset.chain),
|
||||||
padBuffer(asset.address, 32)];
|
padBuffer(asset.address, 32)];
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import {PublicKey} from "@solana/web3.js";
|
||||||
|
import {BRIDGE_ADDRESS, WRAPPED_MASTER} from "../config";
|
||||||
|
import {keccak256} from "ethers/utils";
|
||||||
|
|
||||||
|
// derive the ERC20 address of a Solana SPL asset wrapped on ETH.
|
||||||
|
export function deriveERC20Address(key: PublicKey) {
|
||||||
|
let hashData = "0xff" + BRIDGE_ADDRESS.slice(2);
|
||||||
|
hashData += keccak256(Buffer.concat([new Buffer([1]), key.toBuffer()])).slice(2) // asset_id
|
||||||
|
hashData += keccak256("0x3d602d80600a3d3981f3363d3d373d3d3d363d73" + WRAPPED_MASTER + "5af43d82803e903d91602b57fd5bf3").slice(2) // Bytecode
|
||||||
|
|
||||||
|
return keccak256(hashData).slice(26)
|
||||||
|
}
|
Loading…
Reference in New Issue