diff --git a/client/client.go b/client/client.go index 3f67144..ed861b3 100644 --- a/client/client.go +++ b/client/client.go @@ -2,7 +2,6 @@ package client import ( "context" - "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rpc" @@ -43,8 +42,8 @@ func (ec *Client) SendBundle(ctx context.Context, bundle *SendBundleArgs) (commo return hash, err } -func (ec *Client) BundlePrice(ctx context.Context) (*big.Int, error) { - var bundlePrice big.Int +func (ec *Client) BundlePrice(ctx context.Context) (*BundlePrice, error) { + var bundlePrice BundlePrice err := ec.CallContext(ctx, &bundlePrice, "eth_bundlePrice") return &bundlePrice, err @@ -62,4 +61,4 @@ func (ec *Client) GetStatus(ctx context.Context) (*Status, error) { err := ec.CallContext(ctx, &status, "eth_validatorStatus") return &status, err -} \ No newline at end of file +} diff --git a/client/types.go b/client/types.go index cbf2583..146ef93 100644 --- a/client/types.go +++ b/client/types.go @@ -30,3 +30,9 @@ type Status struct { Status int64 `json:"status"` Validators map[string]int64 `json:"validators"` } + +// BundlePrice is the response for the API `eth_bundlePrice` +type BundlePrice struct { + BundlePrice *big.Int `json:"bundlePrice"` + MinimalGasPrice *big.Int `json:"minimalGasPrice"` +} diff --git a/contract/deposit.sol b/contract/deposit.sol new file mode 100644 index 0000000..e3f0fe8 --- /dev/null +++ b/contract/deposit.sol @@ -0,0 +1,12 @@ +// Solidity files have to start with this pragma. +// It will be used by the Solidity compiler to validate its version. +pragma solidity ^0.7.3; + +contract Deposit { + /** + * A function to deposit value for coinbase. + */ + function deposit() external payable { + block.coinbase.transfer(msg.value); + } +} diff --git a/example/abi/deposit_coinbase.go b/example/abi/deposit_coinbase.go new file mode 100644 index 0000000..d862cca --- /dev/null +++ b/example/abi/deposit_coinbase.go @@ -0,0 +1,3 @@ +package abi + +var DEPOSIT_COINBASE_ABI = `[{"inputs":[],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"}]` diff --git a/example/example.go b/example/example.go index e731f61..0b26f9e 100644 --- a/example/example.go +++ b/example/example.go @@ -20,15 +20,13 @@ import ( "github.com/node-real/go-direct-route/example/utils" ) +// Minnet endpoint var rpcEndPoint = "https://bsc-dataseed.binance.org" - -// Testnet endpoint -//var rpcEndPoint = "https://data-seed-prebsc-1-s1.binance.org:8545" - var directRouteEndPoint = "https://api.nodereal.io/direct-route" // Testnet endpoint -//var directRouteEndPoint = "https://testnet-api.nodereal.io/direct-route" +// var rpcEndPoint = "https://data-seed-prebsc-1-s1.binance.org:8545" +// var directRouteEndPoint = "https://testnet-api.nodereal.io/direct-route" var account1, _ = utils.FromHexKey("input your private key1 here") var account2, _ = utils.FromHexKey("input your private key2 here") @@ -40,7 +38,7 @@ func getBundlePriceDemo() { if err != nil { log.Fatal(fmt.Sprintf("failed to get bundle price %v", err)) } - fmt.Printf("get bundle price price %s\n", price.String()) + fmt.Printf("get bundle price: %s, minmal gas price: %s\n", price.BundlePrice.String(), price.MinimalGasPrice.String()) } func getServiceStatus() { @@ -60,10 +58,14 @@ the two transaction should be all success or all failed. func sendBNBByBundleDemo() { directClient, _ := client.Dial(directRouteEndPoint) rpcClient, _ := ethclient.Dial(rpcEndPoint) - price, err := directClient.BundlePrice(context.Background()) + bundlePrice, err := directClient.BundlePrice(context.Background()) if err != nil { log.Fatalf("failed to get bundle price%v", err) } + price := bundlePrice.BundlePrice + if price.Cmp(bundlePrice.MinimalGasPrice) < 0 { + price = bundlePrice.MinimalGasPrice + } n1, _ := rpcClient.PendingNonceAt(context.Background(), account1.Addr) @@ -125,7 +127,14 @@ we want the bundle been verified on chain during [now+20 second, now+80 second]. func sendBUSDByBundleDemo() { directClient, _ := client.Dial(directRouteEndPoint) rpcClient, _ := ethclient.Dial(rpcEndPoint) - price, _ := directClient.BundlePrice(context.Background()) + bundlePrice, err := directClient.BundlePrice(context.Background()) + if err != nil { + log.Fatalf("failed to get bundle price%v", err) + } + price := bundlePrice.BundlePrice + if price.Cmp(bundlePrice.MinimalGasPrice) < 0 { + price = bundlePrice.MinimalGasPrice + } n1, _ := rpcClient.PendingNonceAt(context.Background(), account1.Addr) @@ -185,6 +194,80 @@ func sendBUSDByBundleDemo() { } } +func sendBNBByBundleWithDepositCoinbaseDemo() { + directClient, _ := client.Dial(directRouteEndPoint) + rpcClient, _ := ethclient.Dial(rpcEndPoint) + bundlePrice, err := directClient.BundlePrice(context.Background()) + if err != nil { + log.Fatalf("failed to get bundle price%v", err) + } + price := bundlePrice.BundlePrice + if price.Cmp(bundlePrice.MinimalGasPrice) < 0 { + price = bundlePrice.MinimalGasPrice + } + n1, _ := rpcClient.PendingNonceAt(context.Background(), account1.Addr) + + chainId := big.NewInt(56) + + // testnet chain id + //chainId := big.NewInt(97) + + valueToTransfer := big.NewInt(100 * params.GWei) + gasLimit := uint64(70000) + depositCoinbaseABI, _ := abi.JSON(strings.NewReader(eabi.DEPOSIT_COINBASE_ABI)) + data1, _ := depositCoinbaseABI.Pack("deposit") + + // testnet contract addr + // depositCoinbaseAddr := common.HexToAddress("0xE7febD44315508a1100E1a06701e7e0Ae5e325Bc") + + // mainnet contract addr + depositCoinbaseAddr := common.HexToAddress("0xB3BB00B9785f35D0BE13B2BD91C8e3742D9Ab03a") + + tx1, hash1, _ := utils.SignTransaction(account1, depositCoinbaseAddr, valueToTransfer, data1, n1, gasLimit, price, chainId) + tx2, hash2, _ := utils.SignTransaction(account1, account2.Addr, valueToTransfer, nil, n1+1, gasLimit, price, chainId) + + maxTime := uint64(time.Now().Unix() + 80) + + bundle := &client.SendBundleArgs{ + Txs: []string{hexutil.Encode(tx1), hexutil.Encode(tx2)}, + MaxBlockNumber: "", + MinTimestamp: nil, + MaxTimestamp: &maxTime, + RevertingTxHashes: nil, + } + bundleHash, err := directClient.SendBundle(context.Background(), bundle) + if err != nil { + log.Fatalf("failed to send bundle %v", err) + } + fmt.Printf("successfull send bundle, hash %v\n", bundleHash) + + queryBundle, err := directClient.GetBundleByHash(context.Background(), bundleHash) + if err != nil || queryBundle == nil { + log.Fatalf("failed to query bundle %v", err) + } + + bz, _ := json.Marshal(queryBundle) + fmt.Printf("The bundle is %s\n", string(bz)) + + found := false + for i := 0; i < 42; i++ { + r1, err1 := rpcClient.TransactionReceipt(context.Background(), hash1) + r2, err2 := rpcClient.TransactionReceipt(context.Background(), hash2) + if r1 != nil && err1 == nil && r2 != nil && err2 == nil { + found = true + break + } + time.Sleep(3 * time.Second) + } + if found { + fmt.Println("bundle verified on chain") + } else { + log.Fatalf("bundle failed to be verified on chain or timeout") + } +} + func main() { - sendBNBByBundleDemo() + //sendBNBByBundleDemo() + //sendBUSDByBundleDemo() + sendBNBByBundleWithDepositCoinbaseDemo() } diff --git a/readme.md b/readme.md index 2ab34f1..242308b 100644 --- a/readme.md +++ b/readme.md @@ -85,6 +85,14 @@ You can try Direct-Route Testnet by changing the endpoint to: `https://testnet-a Please note you should use the BSC testnet RPC and use testnet chainId(97) when sign the transaction. +### Deposit coinbase contract + +If you want to increase your *Bundle Price*, you can use this contract, which will transfer your provided *BNB* to the coinbase responsible for mining your bundle. +please refer the `sendBNBByBundleWithDepositCoinbaseDemo()` in example.go. + +1. mainnet: `0xB3BB00B9785f35D0BE13B2BD91C8e3742D9Ab03a` +2. testnet: `0xE7febD44315508a1100E1a06701e7e0Ae5e325Bc` + ### SDK Example We provide three demos in `example.go`: @@ -98,6 +106,9 @@ to each other, the second transaction is allowed to be failed, and the bundle should be verified on chain during [now+20 second, now+80 second]. This case shows you how to interact with smart contract through direct-route, and how to control the timing to be verified. +4. `sendBNBByBundleWithDepositCoinbaseDemo()`. In this case, we use two different +accounts to send BNB to each other, and including deposit BNB to coinbase to increase *Bundle Price* the two transactions should be all +successful or all failed. If you want to try with above examples, what you need to do is just to replace the private keys of `account1` and `account2` in `example.go`