Skip to content

Commit

Permalink
feat: bsc cross chain (#239)
Browse files Browse the repository at this point in the history
* feat: cross chain multi messages and executor
  • Loading branch information
BarryTong65 authored Apr 25, 2024
1 parent 1e23230 commit a88b8a1
Show file tree
Hide file tree
Showing 19 changed files with 1,872 additions and 1 deletion.
171 changes: 171 additions & 0 deletions bsc/api_basic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package bsc

import (
"context"
"log"
"math/big"
"strings"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"

"github.com/bnb-chain/greenfield-go-sdk/bsctypes"
bsccommon "github.com/bnb-chain/greenfield-go-sdk/common"
)

type IBasicClient interface {
SendTx(ctx context.Context, nonce uint64, toAddr *common.Address, amount *big.Int, gasPrice *big.Int, data []byte) (*common.Hash, error)
GetDeployment() *bsctypes.Deployment
GetMinAckRelayFee(ctx context.Context) (relayFee *big.Int, minAckRelayFee *big.Int, err error)
GetCallbackGasPrice(ctx context.Context) (gasPrice *big.Int, err error)
CheckTxStatus(ctx context.Context, tx *common.Hash) (bool, error)
}

func (c *Client) GetDeployment() *bsctypes.Deployment {
return c.deployment
}

func (c *Client) SendTx(ctx context.Context, nonce uint64, toAddr *common.Address, amount *big.Int, gasPrice *big.Int, data []byte) (*common.Hash, error) {
if nonce == 0 {
n, err := c.chainClient.PendingNonceAt(ctx, *c.defaultAccount.GetAddress())
if err != nil {
return nil, err
}
nonce = n
}
gasLimit := uint64(5e6) // Assuming the gas limit is static, adjust as necessary
var err error
if gasPrice == nil {
gasPrice, err = c.chainClient.SuggestGasPrice(ctx)
if err != nil {
return nil, err
}
}

// Create the transaction using NewTx instead of the deprecated NewTransaction
tx := types.NewTx(&types.LegacyTx{
Nonce: nonce,
To: toAddr,
Value: amount,
Gas: gasLimit,
GasPrice: gasPrice,
Data: data,
})

chainId, err := c.chainClient.ChainID(ctx)
if err != nil {
return nil, err
}
signedTx, err := types.SignTx(tx, types.NewLondonSigner(chainId), c.defaultAccount.GetKeyManager().GetPrivateKey())
if err != nil {
return nil, err
}
err = c.chainClient.SendTransaction(ctx, signedTx)
if err != nil {
return nil, err
}
hash := signedTx.Hash()
return &hash, nil
}

func (c *Client) GetMinAckRelayFee(ctx context.Context) (relayFee *big.Int, minAckRelayFee *big.Int, err error) {
var (
ok1 bool
ok2 bool
)
parsedABI, err := abi.JSON(strings.NewReader(bsccommon.CrossChainABI))
if err != nil {
log.Fatalf("failed to parse contract ABI: %v", err)
}

packedData, err := parsedABI.Pack("getRelayFees")
if err != nil {
log.Fatalf("failed to pack data for getRelayFees: %v", err)
}

contractAddress := common.HexToAddress(c.GetDeployment().CrossChain)
msg := ethereum.CallMsg{
To: &contractAddress,
Data: packedData,
}

resp, err := c.chainClient.CallContract(ctx, msg, nil)
if err != nil {
log.Fatalf("failed to call contract: %v", err)
}

result, err := parsedABI.Unpack("getRelayFees", resp)
if err != nil {
log.Fatalf("failed to unpack returned data: %v", err)
}

if len(result) != 2 {
log.Fatalf("expected two return values from getRelayFees")
}

relayFee, ok1 = result[0].(*big.Int)
minAckRelayFee, ok2 = result[1].(*big.Int)
if !ok1 || !ok2 {
log.Fatalf("type assertion failed for one or both return values")
}

return relayFee, minAckRelayFee, nil
}

func (c *Client) GetCallbackGasPrice(ctx context.Context) (gasPrice *big.Int, err error) {
var (
ok bool
)
parsedABI, err := abi.JSON(strings.NewReader(bsccommon.CrossChainABI))
if err != nil {
log.Fatalf("failed to parse contract ABI: %v", err)
}

packedData, err := parsedABI.Pack("callbackGasPrice")
if err != nil {
log.Fatalf("failed to pack data for callbackGasPrice: %v", err)
}

contractAddress := common.HexToAddress(c.GetDeployment().CrossChain)
msg := ethereum.CallMsg{
To: &contractAddress,
Data: packedData,
}

resp, err := c.chainClient.CallContract(ctx, msg, nil)
if err != nil {
log.Fatalf("failed to call contract: %v", err)
}

result, err := parsedABI.Unpack("callbackGasPrice", resp)
if err != nil {
log.Fatalf("failed to unpack returned data: %v", err)
}

if len(result) != 1 {
log.Fatalf("expected one return values from callbackGasPrice")
}

gasPrice, ok = result[0].(*big.Int)
if !ok {
log.Fatalf("type assertion failed for one or both return values")
}

return gasPrice, nil
}

func (c *Client) CheckTxStatus(ctx context.Context, tx *common.Hash) (bool, error) {
var success bool

receipt, err := c.chainClient.TransactionReceipt(ctx, *tx)
if err != nil {
log.Fatal(err)
}
if receipt.Status == 1 {
success = true
}

return success, nil
}
37 changes: 37 additions & 0 deletions bsc/api_greenfield_executor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package bsc

import (
"context"
"log"
"strings"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"

"github.com/bnb-chain/greenfield-go-sdk/bsctypes"
bsccommon "github.com/bnb-chain/greenfield-go-sdk/common"
)

type IGreenfieldExecutorClient interface {
Execute(ctx context.Context, message *bsctypes.ExecutorMessages) (*common.Hash, error)
}

func (c *Client) Execute(ctx context.Context, message *bsctypes.ExecutorMessages) (*common.Hash, error) {
parsedABI, err := abi.JSON(strings.NewReader(bsccommon.ExecutorABI))
if err != nil {
log.Fatalf("failed to parse contract ABI: %v", err)
}

packedData, err := parsedABI.Pack("execute", message.MsgTypes, message.MsgBytes)
if err != nil {
log.Fatalf("failed to pack data for execute: %v", err)
}

contractAddress := common.HexToAddress(c.GetDeployment().GreenfieldExecutor)
tx, err := c.SendTx(ctx, 0, &contractAddress, message.RelayFee, nil, packedData)
if err != nil {
log.Fatalf("failed to call contract: %v", err)
}

return tx, nil
}
43 changes: 43 additions & 0 deletions bsc/api_multi_messages.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package bsc

import (
"context"
"log"
"math/big"
"strings"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"

"github.com/bnb-chain/greenfield-go-sdk/bsctypes"
bsccommon "github.com/bnb-chain/greenfield-go-sdk/common"
)

type IMultiMessageClient interface {
SendMessages(ctx context.Context, message *bsctypes.MultiMessage) (*common.Hash, error)
}

func (c *Client) SendMessages(ctx context.Context, message *bsctypes.MultiMessage) (*common.Hash, error) {
parsedABI, err := abi.JSON(strings.NewReader(bsccommon.MultiMessageABI))
if err != nil {
log.Fatalf("failed to parse contract ABI: %v", err)
}

packedData, err := parsedABI.Pack("sendMessages", message.Targets, message.Data, message.Values)
if err != nil {
log.Fatalf("failed to pack data for sendMessages: %v", err)
}

sum := new(big.Int)
for _, value := range message.Values {
sum.Add(sum, value)
}

contractAddress := common.HexToAddress(c.GetDeployment().MultiMessage)
tx, err := c.SendTx(ctx, 0, &contractAddress, sum, nil, packedData)
if err != nil {
log.Fatalf("failed to call contract: %v", err)
}

return tx, nil
}
110 changes: 110 additions & 0 deletions bsc/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package bsc

import (
"encoding/json"
"errors"
"io"
"log"
"net/http"
"os"

"github.com/ethereum/go-ethereum/ethclient"
"google.golang.org/grpc"

"github.com/bnb-chain/greenfield-go-sdk/bsctypes"
)

// IClient - Declare all BSC SDK Client APIs, including APIs for multi messages & greenfield executor
type IClient interface {
IMultiMessageClient
IGreenfieldExecutorClient
IBasicClient
}

// Client - The implementation for IClient, implement all Client APIs for Greenfield SDK.
type Client struct {
// The chain Client is used to interact with the blockchain
chainClient *ethclient.Client
// The HTTP Client is used to send HTTP requests to the greenfield blockchain and sp
httpClient *http.Client
// The default account to use when sending transactions.
defaultAccount *bsctypes.BscAccount
// Whether the connection to the blockchain node is secure (HTTPS) or not (HTTP).
secure bool
// Host is the target sp server hostname,it is the host info in the request which sent to SP
host string
rpcURL string
deployment *bsctypes.Deployment
}

// Option - Configurations for providing optional parameters for the Binance Smart Chain SDK Client.
type Option struct {
// GrpcDialOption is the list of gRPC dial options used to configure the connection to the blockchain node.
GrpcDialOption grpc.DialOption
// DefaultAccount is the default account of Client.
DefaultAccount *bsctypes.BscAccount
// Secure is a flag that specifies whether the Client should use HTTPS or not.
Secure bool
// Transport is the HTTP transport used to send requests to the storage provider endpoint.
Transport http.RoundTripper
// Host is the target sp server hostname.
Host string
}

func New(rpcURL string, env string, option Option) (IClient, error) {
if rpcURL == "" {
return nil, errors.New("fail to get grpcAddress and chainID to construct Client")
}
var (
cc *ethclient.Client
deployment *bsctypes.Deployment
path string
err error
)
cc, err = ethclient.Dial(rpcURL)
if err != nil {
return nil, err
}

switch env {
case "dev-net":
path = "./common/contract/dev-net.json"
case "qa-net":
path = "./common/contract/qa-net.json"
case "test-net":
path = "./common/contract/test-net.json"
case "main-net":
path = "./common/contract/main-net.json"
}

jsonFile, err := os.Open(path)
if err != nil {
log.Fatalf("failed to open JSON file: %v", err)
}
defer jsonFile.Close()

// Read the JSON file into a byte slice
byteValue, err := io.ReadAll(jsonFile)
if err != nil {
log.Fatalf("failed to read JSON file: %v", err)
return nil, err
}

err = json.Unmarshal(byteValue, &deployment)
if err != nil {
log.Fatalf("failed to unmarshal JSON data: %v", err)
return nil, err
}

c := Client{
chainClient: cc,
httpClient: &http.Client{Transport: option.Transport},
defaultAccount: option.DefaultAccount, // it allows to be nil
secure: option.Secure,
host: option.Host,
rpcURL: rpcURL,
deployment: deployment,
}

return &c, nil
}
Loading

0 comments on commit a88b8a1

Please sign in to comment.