-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: cross chain multi messages and executor
- Loading branch information
1 parent
1e23230
commit a88b8a1
Showing
19 changed files
with
1,872 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.