Files
zeekay 579f53c3cc chore: remove local replace directives and update dependencies
- 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
2026-01-03 07:09:01 -08:00

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")
}