mirror of
https://github.com/luxfi/cli.git
synced 2026-10-30 17:01:20 +00:00
- Update github.com/luxfi/netrunner v1.15.3 → v1.15.4 - Update github.com/luxfi/node v1.22.77 → v1.22.80 - Remove local replace directive for netrunner - Use published package versions for reproducible builds
588 lines
14 KiB
Go
588 lines
14 KiB
Go
// Copyright (C) 2022-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package warp
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
"time"
|
|
|
|
"github.com/luxfi/cli/pkg/ux"
|
|
"github.com/luxfi/crypto"
|
|
"github.com/luxfi/geth/core/types"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/sdk/contract"
|
|
)
|
|
|
|
type EndpointKind int64
|
|
|
|
const (
|
|
Undefined EndpointKind = iota
|
|
ERC20TokenHome
|
|
NativeTokenHome
|
|
ERC20TokenRemote
|
|
NativeTokenRemote
|
|
)
|
|
|
|
func GetEndpointKind(
|
|
rpcURL string,
|
|
address crypto.Address,
|
|
) (EndpointKind, error) {
|
|
if _, err := ERC20TokenHomeGetTokenAddress(rpcURL, address); err == nil {
|
|
return ERC20TokenHome, nil
|
|
}
|
|
if _, err := NativeTokenHomeGetTokenAddress(rpcURL, address); err == nil {
|
|
return NativeTokenHome, nil
|
|
}
|
|
if _, err := NativeTokenRemoteGetTotalNativeAssetSupply(rpcURL, address); err == nil {
|
|
return NativeTokenRemote, nil
|
|
}
|
|
_, err := ERC20TokenRemoteGetTokenHomeAddress(rpcURL, address)
|
|
if err == nil {
|
|
return ERC20TokenRemote, nil
|
|
}
|
|
return Undefined, err
|
|
}
|
|
|
|
func ERC20TokenHomeGetTokenAddress(
|
|
rpcURL string,
|
|
address crypto.Address,
|
|
) (crypto.Address, error) {
|
|
out, err := contract.CallToMethod(
|
|
rpcURL,
|
|
address,
|
|
"token()->(address)",
|
|
)
|
|
if err != nil {
|
|
return crypto.Address{}, err
|
|
}
|
|
return contract.GetSmartContractCallResult[crypto.Address]("token", out)
|
|
}
|
|
|
|
func NativeTokenHomeGetTokenAddress(
|
|
rpcURL string,
|
|
address crypto.Address,
|
|
) (crypto.Address, error) {
|
|
out, err := contract.CallToMethod(
|
|
rpcURL,
|
|
address,
|
|
"wrappedToken()->(address)",
|
|
)
|
|
if err != nil {
|
|
return crypto.Address{}, err
|
|
}
|
|
return contract.GetSmartContractCallResult[crypto.Address]("wrappedToken", out)
|
|
}
|
|
|
|
func TokenRemoteIsCollateralized(
|
|
rpcURL string,
|
|
address crypto.Address,
|
|
) (bool, error) {
|
|
out, err := contract.CallToMethod(
|
|
rpcURL,
|
|
address,
|
|
"isCollateralized()->(bool)",
|
|
)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return contract.GetSmartContractCallResult[bool]("isCollateralized", out)
|
|
}
|
|
|
|
func TokenHomeGetDecimals(
|
|
rpcURL string,
|
|
address crypto.Address,
|
|
) (uint8, error) {
|
|
out, err := contract.CallToMethod(
|
|
rpcURL,
|
|
address,
|
|
"tokenDecimals()->(uint8)",
|
|
)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return contract.GetSmartContractCallResult[uint8]("tokenDecimals", out)
|
|
}
|
|
|
|
type RegisteredRemote struct {
|
|
Registered bool
|
|
CollateralNeeded *big.Int
|
|
TokenMultiplier *big.Int
|
|
MultiplyOnRemote bool
|
|
}
|
|
|
|
func TokenHomeGetRegisteredRemote(
|
|
rpcURL string,
|
|
address crypto.Address,
|
|
remoteBlockchainID [32]byte,
|
|
remoteAddress crypto.Address,
|
|
) (RegisteredRemote, error) {
|
|
out, err := contract.CallToMethod(
|
|
rpcURL,
|
|
address,
|
|
"registeredRemotes(bytes32, address)->(bool,uint256,uint256,bool)",
|
|
remoteBlockchainID,
|
|
remoteAddress,
|
|
)
|
|
if err != nil {
|
|
return RegisteredRemote{}, err
|
|
}
|
|
var (
|
|
registeredRemote RegisteredRemote
|
|
b bool
|
|
)
|
|
if len(out) != 4 {
|
|
return RegisteredRemote{}, fmt.Errorf("error at registeredRemotes call, expected 4 return values, got %d", len(out))
|
|
}
|
|
registeredRemote.Registered, b = out[0].(bool)
|
|
if !b {
|
|
return RegisteredRemote{}, fmt.Errorf("error at registeredRemotes call, expected bool, got %T", out[0])
|
|
}
|
|
registeredRemote.CollateralNeeded, b = out[1].(*big.Int)
|
|
if !b {
|
|
return RegisteredRemote{}, fmt.Errorf("error at registeredRemotes call, expected *big.Int, got %T", out[1])
|
|
}
|
|
registeredRemote.TokenMultiplier, b = out[2].(*big.Int)
|
|
if !b {
|
|
return RegisteredRemote{}, fmt.Errorf("error at registeredRemotes call, expected *big.Int, got %T", out[2])
|
|
}
|
|
registeredRemote.MultiplyOnRemote, b = out[3].(bool)
|
|
if !b {
|
|
return RegisteredRemote{}, fmt.Errorf("error at registeredRemotes call, expected bool, got %T", out[3])
|
|
}
|
|
return registeredRemote, nil
|
|
}
|
|
|
|
func ERC20TokenRemoteGetTokenHomeAddress(
|
|
rpcURL string,
|
|
address crypto.Address,
|
|
) (crypto.Address, error) {
|
|
out, err := contract.CallToMethod(
|
|
rpcURL,
|
|
address,
|
|
"tokenHomeAddress()->(address)",
|
|
)
|
|
if err != nil {
|
|
return crypto.Address{}, err
|
|
}
|
|
return contract.GetSmartContractCallResult[crypto.Address]("tokenHomeAddress", out)
|
|
}
|
|
|
|
func NativeTokenRemoteGetTotalNativeAssetSupply(
|
|
rpcURL string,
|
|
address crypto.Address,
|
|
) (*big.Int, error) {
|
|
out, err := contract.CallToMethod(
|
|
rpcURL,
|
|
address,
|
|
"totalNativeAssetSupply()->(uint256)",
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return contract.GetSmartContractCallResult[*big.Int]("totalNativeAssetSupply", out)
|
|
}
|
|
|
|
func ERC20TokenHomeSend(
|
|
rpcURL string,
|
|
homeAddress crypto.Address,
|
|
privateKey string,
|
|
destinationBlockchainID ids.ID,
|
|
destinationWarpEndpoint crypto.Address,
|
|
amountRecipient crypto.Address,
|
|
amount *big.Int,
|
|
) (*types.Receipt, *types.Receipt, error) {
|
|
type Params struct {
|
|
DestinationBlockchainID [32]byte
|
|
DestinationWarpEndpoint crypto.Address
|
|
AmountRecipient crypto.Address
|
|
PrimaryFeeTokenAddress crypto.Address
|
|
PrimaryFee *big.Int
|
|
SecondaryFee *big.Int
|
|
RequiredGasLimit *big.Int
|
|
MultiHopFallback crypto.Address
|
|
}
|
|
tokenAddress, err := ERC20TokenHomeGetTokenAddress(rpcURL, homeAddress)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
_, receipt, err := contract.TxToMethod(
|
|
rpcURL,
|
|
false,
|
|
crypto.Address{},
|
|
privateKey,
|
|
tokenAddress,
|
|
nil,
|
|
"erc20 token approve",
|
|
nil,
|
|
"approve(address, uint256)->(bool)",
|
|
homeAddress,
|
|
amount,
|
|
)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
params := Params{
|
|
DestinationBlockchainID: destinationBlockchainID,
|
|
DestinationWarpEndpoint: destinationWarpEndpoint,
|
|
AmountRecipient: amountRecipient,
|
|
PrimaryFeeTokenAddress: tokenAddress, // in theory this is optional
|
|
PrimaryFee: big.NewInt(0),
|
|
SecondaryFee: big.NewInt(0),
|
|
RequiredGasLimit: big.NewInt(250000),
|
|
MultiHopFallback: crypto.Address{},
|
|
}
|
|
_, receipt2, err := contract.TxToMethod(
|
|
rpcURL,
|
|
false,
|
|
crypto.Address{},
|
|
privateKey,
|
|
homeAddress,
|
|
nil,
|
|
"erc20 token home send",
|
|
nil,
|
|
"send((bytes32, address, address, address, uint256, uint256, uint256, address), uint256)",
|
|
params,
|
|
amount,
|
|
)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return receipt, receipt2, nil
|
|
}
|
|
|
|
func NativeTokenHomeSend(
|
|
rpcURL string,
|
|
homeAddress crypto.Address,
|
|
privateKey string,
|
|
destinationBlockchainID ids.ID,
|
|
destinationWarpEndpoint crypto.Address,
|
|
amountRecipient crypto.Address,
|
|
amount *big.Int,
|
|
) (*types.Receipt, error) {
|
|
type Params struct {
|
|
DestinationBlockchainID [32]byte
|
|
DestinationWarpEndpoint crypto.Address
|
|
AmountRecipient crypto.Address
|
|
PrimaryFeeTokenAddress crypto.Address
|
|
PrimaryFee *big.Int
|
|
SecondaryFee *big.Int
|
|
RequiredGasLimit *big.Int
|
|
MultiHopFallback crypto.Address
|
|
}
|
|
tokenAddress, err := NativeTokenHomeGetTokenAddress(rpcURL, homeAddress)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params := Params{
|
|
DestinationBlockchainID: destinationBlockchainID,
|
|
DestinationWarpEndpoint: destinationWarpEndpoint,
|
|
AmountRecipient: amountRecipient,
|
|
PrimaryFeeTokenAddress: tokenAddress, // in theory this is optional
|
|
PrimaryFee: big.NewInt(0),
|
|
SecondaryFee: big.NewInt(0),
|
|
RequiredGasLimit: big.NewInt(250000),
|
|
MultiHopFallback: crypto.Address{},
|
|
}
|
|
_, receipt, err := contract.TxToMethod(
|
|
rpcURL,
|
|
false,
|
|
crypto.Address{},
|
|
privateKey,
|
|
homeAddress,
|
|
amount,
|
|
"native token home send",
|
|
nil,
|
|
"send((bytes32, address, address, address, uint256, uint256, uint256, address))",
|
|
params,
|
|
)
|
|
return receipt, err
|
|
}
|
|
|
|
func ERC20TokenRemoteSend(
|
|
rpcURL string,
|
|
remoteAddress crypto.Address,
|
|
privateKey string,
|
|
destinationBlockchainID ids.ID,
|
|
destinationWarpEndpoint crypto.Address,
|
|
amountRecipient crypto.Address,
|
|
amount *big.Int,
|
|
) (*types.Receipt, *types.Receipt, error) {
|
|
_, receipt, err := contract.TxToMethod(
|
|
rpcURL,
|
|
false,
|
|
crypto.Address{},
|
|
privateKey,
|
|
remoteAddress,
|
|
nil,
|
|
"erc20 token remote approve",
|
|
nil,
|
|
"approve(address, uint256)->(bool)",
|
|
remoteAddress,
|
|
amount,
|
|
)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
type Params struct {
|
|
DestinationBlockchainID [32]byte
|
|
DestinationWarpEndpoint crypto.Address
|
|
AmountRecipient crypto.Address
|
|
PrimaryFeeTokenAddress crypto.Address
|
|
PrimaryFee *big.Int
|
|
SecondaryFee *big.Int
|
|
RequiredGasLimit *big.Int
|
|
MultiHopFallback crypto.Address
|
|
}
|
|
params := Params{
|
|
DestinationBlockchainID: destinationBlockchainID,
|
|
DestinationWarpEndpoint: destinationWarpEndpoint,
|
|
AmountRecipient: amountRecipient,
|
|
PrimaryFeeTokenAddress: crypto.Address{},
|
|
PrimaryFee: big.NewInt(0),
|
|
SecondaryFee: big.NewInt(0),
|
|
RequiredGasLimit: big.NewInt(250000),
|
|
MultiHopFallback: crypto.Address{},
|
|
}
|
|
_, receipt2, err := contract.TxToMethod(
|
|
rpcURL,
|
|
false,
|
|
crypto.Address{},
|
|
privateKey,
|
|
remoteAddress,
|
|
nil,
|
|
"erc20 token remote send",
|
|
nil,
|
|
"send((bytes32, address, address, address, uint256, uint256, uint256, address), uint256)",
|
|
params,
|
|
amount,
|
|
)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return receipt, receipt2, err
|
|
}
|
|
|
|
func NativeTokenRemoteSend(
|
|
rpcURL string,
|
|
remoteAddress crypto.Address,
|
|
privateKey string,
|
|
destinationBlockchainID ids.ID,
|
|
destinationWarpEndpoint crypto.Address,
|
|
amountRecipient crypto.Address,
|
|
amount *big.Int,
|
|
) (*types.Receipt, error) {
|
|
type Params struct {
|
|
DestinationBlockchainID [32]byte
|
|
DestinationWarpEndpoint crypto.Address
|
|
AmountRecipient crypto.Address
|
|
PrimaryFeeTokenAddress crypto.Address
|
|
PrimaryFee *big.Int
|
|
SecondaryFee *big.Int
|
|
RequiredGasLimit *big.Int
|
|
MultiHopFallback crypto.Address
|
|
}
|
|
params := Params{
|
|
DestinationBlockchainID: destinationBlockchainID,
|
|
DestinationWarpEndpoint: destinationWarpEndpoint,
|
|
AmountRecipient: amountRecipient,
|
|
PrimaryFeeTokenAddress: remoteAddress, // in theory this is optional
|
|
PrimaryFee: big.NewInt(0),
|
|
SecondaryFee: big.NewInt(0),
|
|
RequiredGasLimit: big.NewInt(250000),
|
|
MultiHopFallback: crypto.Address{},
|
|
}
|
|
_, receipt, err := contract.TxToMethod(
|
|
rpcURL,
|
|
false,
|
|
crypto.Address{},
|
|
privateKey,
|
|
remoteAddress,
|
|
amount,
|
|
"native token remote send",
|
|
nil,
|
|
"send((bytes32, address, address, address, uint256, uint256, uint256, address))",
|
|
params,
|
|
)
|
|
return receipt, err
|
|
}
|
|
|
|
func NativeTokenHomeAddCollateral(
|
|
rpcURL string,
|
|
homeAddress crypto.Address,
|
|
privateKey string,
|
|
remoteBlockchainID [32]byte,
|
|
remoteAddress crypto.Address,
|
|
amount *big.Int,
|
|
) error {
|
|
_, _, err := contract.TxToMethod(
|
|
rpcURL,
|
|
false,
|
|
crypto.Address{},
|
|
privateKey,
|
|
homeAddress,
|
|
amount,
|
|
"native token home add collateral",
|
|
nil,
|
|
"addCollateral(bytes32, address)",
|
|
remoteBlockchainID,
|
|
remoteAddress,
|
|
)
|
|
return err
|
|
}
|
|
|
|
func ERC20TokenHomeAddCollateral(
|
|
rpcURL string,
|
|
homeAddress crypto.Address,
|
|
privateKey string,
|
|
remoteBlockchainID [32]byte,
|
|
remoteAddress crypto.Address,
|
|
amount *big.Int,
|
|
) error {
|
|
tokenAddress, err := ERC20TokenHomeGetTokenAddress(rpcURL, homeAddress)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, _, err := contract.TxToMethod(
|
|
rpcURL,
|
|
false,
|
|
crypto.Address{},
|
|
privateKey,
|
|
tokenAddress,
|
|
nil,
|
|
"erc20 token home approve",
|
|
nil,
|
|
"approve(address, uint256)->(bool)",
|
|
homeAddress,
|
|
amount,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
// Use separate API calls for reliability - wait for the first transaction to complete
|
|
time.Sleep(5 * time.Second)
|
|
_, _, err = contract.TxToMethod(
|
|
rpcURL,
|
|
false,
|
|
crypto.Address{},
|
|
privateKey,
|
|
homeAddress,
|
|
nil,
|
|
"erc20 token home add collateral",
|
|
nil,
|
|
"addCollateral(bytes32, address, uint256)",
|
|
remoteBlockchainID,
|
|
remoteAddress,
|
|
amount,
|
|
)
|
|
return err
|
|
}
|
|
|
|
func TokenHomeAddCollateral(
|
|
rpcURL string,
|
|
homeAddress crypto.Address,
|
|
privateKey string,
|
|
remoteBlockchainID [32]byte,
|
|
remoteAddress crypto.Address,
|
|
amount *big.Int,
|
|
) error {
|
|
ux.Logger.PrintToUser("Collateralizing remote contract on the home chain")
|
|
endpointKind, err := GetEndpointKind(
|
|
rpcURL,
|
|
homeAddress,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch endpointKind {
|
|
case ERC20TokenHome:
|
|
return ERC20TokenHomeAddCollateral(
|
|
rpcURL,
|
|
homeAddress,
|
|
privateKey,
|
|
remoteBlockchainID,
|
|
remoteAddress,
|
|
amount,
|
|
)
|
|
case NativeTokenHome:
|
|
return NativeTokenHomeAddCollateral(
|
|
rpcURL,
|
|
homeAddress,
|
|
privateKey,
|
|
remoteBlockchainID,
|
|
remoteAddress,
|
|
amount,
|
|
)
|
|
case ERC20TokenRemote:
|
|
return fmt.Errorf("trying to add collateral to an erc20 token remote endpoint")
|
|
case NativeTokenRemote:
|
|
return fmt.Errorf("trying to add collateral to a native token remote endpoint")
|
|
}
|
|
return fmt.Errorf("unknown warp endpoint")
|
|
}
|
|
|
|
func Send(
|
|
rpcURL string,
|
|
address crypto.Address,
|
|
privateKey string,
|
|
destinationBlockchainID ids.ID,
|
|
destinationAddress crypto.Address,
|
|
amountRecipient crypto.Address,
|
|
amount *big.Int,
|
|
) (*types.Receipt, *types.Receipt, error) {
|
|
endpointKind, err := GetEndpointKind(
|
|
rpcURL,
|
|
address,
|
|
)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
switch endpointKind {
|
|
case ERC20TokenRemote:
|
|
return ERC20TokenRemoteSend(
|
|
rpcURL,
|
|
address,
|
|
privateKey,
|
|
destinationBlockchainID,
|
|
destinationAddress,
|
|
amountRecipient,
|
|
amount,
|
|
)
|
|
case ERC20TokenHome:
|
|
return ERC20TokenHomeSend(
|
|
rpcURL,
|
|
address,
|
|
privateKey,
|
|
destinationBlockchainID,
|
|
destinationAddress,
|
|
amountRecipient,
|
|
amount,
|
|
)
|
|
case NativeTokenHome:
|
|
receipt, err := NativeTokenHomeSend(
|
|
rpcURL,
|
|
address,
|
|
privateKey,
|
|
destinationBlockchainID,
|
|
destinationAddress,
|
|
amountRecipient,
|
|
amount,
|
|
)
|
|
return receipt, nil, err
|
|
case NativeTokenRemote:
|
|
receipt, err := NativeTokenRemoteSend(
|
|
rpcURL,
|
|
address,
|
|
privateKey,
|
|
destinationBlockchainID,
|
|
destinationAddress,
|
|
amountRecipient,
|
|
amount,
|
|
)
|
|
return receipt, nil, err
|
|
}
|
|
return nil, nil, fmt.Errorf("unknown warp endpoint")
|
|
}
|