Skip to content

Commit

Permalink
Merge branch 'feat-chain'
Browse files Browse the repository at this point in the history
  • Loading branch information
iosguang committed May 7, 2022
2 parents 8431d90 + a7f4b7c commit 6b6dffb
Show file tree
Hide file tree
Showing 77 changed files with 4,274 additions and 2,957 deletions.
147 changes: 137 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,146 @@
# wallet-SDK

ComingChat substrate wallet SDK

| | Bitcoin | Ethereum | Polka |
| --------------------- | ------- | -------- | ----- |
| import mnemonic ||||
| import keystore ||||
| pri/pub key & address ||||
| multi network ||||
| publicKey to address ||||
| address to publicKey ||||
| sign data | ☑️ | ☑️ ||

| import mnemonic ||||
| import keystore ||||
| pri/pub key & address ||||
| multi network ||||
| publicKey to address ||||
| address to publicKey ||||
| sign data | ☑️ | ☑️ ||
| | | | |
| query balance ||||
| fetch transaction detail ||||
| gas fee ||||
| send taw transaction || ✅ ☑️ | ✅ ☑️ |
| multi token || ✅ erc20 | ✅ XBTC |

*If there are two icons, the first icon indicates development status, and the second icon indicates test status.*
✅: Completed ☑️: TODO ❌: Unsupported

## Usage

### About Wallet

SDK provide wallet import, account public and private key and address acquisition.

#### Import Wallet

```golang
// import mnemonic
wallet, err = NewWalletFromMnemonic(mnemonic)

// import keystore
// It only supports Polka keystore.
wallet, err = NewWalletFromKeyStore(keyStoreJson, password)
```

#### Create Account

We currently support accounts in Bitcoin, Ethereum and Polkadot ecosystems.

```golang
// Polka
polkaAccount, err = wallet.GetOrCreatePolkaAccount(network)

// Bitcoin
bitcoinAccount, err = wallet.GetOrCreateBitcoinAccount(chainnet)

// Ethereum
ethereumAccount, err = wallet.GetOrCreateEthereumAccount()
```

#### Get PrivateKey, PublicKey, Address

```golang
privateData, err = account.PrivateKeyData()

privateKey, err = account.PrivateKey()

publicKey = account.PublicKey()

address = account.Address()
```


### About Chain

We can use chain tools to do chain related work.
* query balance
* query estimate fees
* send transaction
* fetch transaction detail
* support multi token:
* eth contract erc20 token
* xbtc

#### Create Chain

```golang
polkaChain, err = polka.NewChainWithRpc(rpcUrl, scanUrl, network)

bitcoinChain, err = btc.NewChainWithChainnet(chainnet)

ethereumChain, err = eth.NewChainWithRpc(rpcUrl)
```

#### Methods

```golang

// query balance
balance, err = chain.BalanceOfAddress(address)
balance, err = chain.BalanceOfPublicKey(publicKey)
balance, err = chain.BalanceOfAccount(account)

// send transaction
txHash, err = chain.SendRawTransaction(signedTx)

// fetch transaction detail
detail, err = chain.FetchTransactionDetail(hashString)
status = chain.FetchTransactionStatus(hashString)
```

#### Chain's Token

```golang
// MainToken
token = chain.MainToken()

// btc have not tokens

// polka (only support XBTC of ChainX currently)
xbtcToken = polkaChain.XBTCToken()

// ethereum erc20 token
erc20Token = ethereumChain.Erc20Token(contractAddress)

// token balance (similar to chain's balance)
balance, err = anyToken.BalanceOfAddress(address)
balance, err = anyToken.BalanceOfPublicKey(publicKey)
balance, err = anyToken.BalanceOfAccount(account)
```

#### Estimate fee

```golang
// sbtc's estimate fee is compute by utxo

// polka
transaction = // ...
fee, err = polkaChain.EstimateFeeForTransaction(transaction)

// ethereum
gasPrice, err = ethChain.SuggestGasPrice()
gasLimit, err = anyEthToken.EstimateGasLimit(fromAddress, receiverAddress, gasPrice, amount)
```

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

ComingChat substrate wallet SDK

## build android && ios

* make buildAllAndroid
Expand Down
13 changes: 13 additions & 0 deletions core/base/balance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package base

type Balance struct {
Total string
Usable string
}

func EmptyBalance() *Balance {
return &Balance{
Total: "0",
Usable: "0",
}
}
33 changes: 33 additions & 0 deletions core/base/chain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package base

type Chain interface {
MainToken() Token

BalanceOfAddress(address string) (*Balance, error)
BalanceOfPublicKey(publicKey string) (*Balance, error)
BalanceOfAccount(account Account) (*Balance, error)

// Send the raw transaction on-chain
// @return the hex hash string
SendRawTransaction(signedTx string) (string, error)

// Fetch transaction details through transaction hash
FetchTransactionDetail(hash string) (*TransactionDetail, error)

// Fetch transaction status through transaction hash
FetchTransactionStatus(hash string) TransactionStatus

// Batch fetch the transaction status, the hash list and the return value,
// which can only be passed as strings separated by ","
// @param hashListString The hash of the transactions to be queried in batches, a string concatenated with ",": "hash1,hash2,hash3"
// @return Batch transaction status, its order is consistent with hashListString: "status1,status2,status3"
BatchFetchTransactionStatus(hashListString string) string

// -----------------------------
// polka
// GetSignDataFromChain(t *Transaction, walletAddress string) ([]byte, error)

// EstimateFeeForTransaction(transaction *Transaction) (s string, err error)

// FetchScriptHashForMiniX(transferTo, amount string) (*MiniXScriptHash, error)
}
6 changes: 6 additions & 0 deletions core/base/error.go
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
package base

import "errors"

var (
ErrUnsupportedFunction = errors.New("This chain does not support this feature.")
)
17 changes: 17 additions & 0 deletions core/base/token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package base

type TokenInfo struct {
Name string
Symbol string
Decimal int16
}

type Token interface {
Chain() Chain

TokenInfo() (*TokenInfo, error)

BalanceOfAddress(address string) (*Balance, error)
BalanceOfPublicKey(publicKey string) (*Balance, error)
BalanceOfAccount(account Account) (*Balance, error)
}
35 changes: 35 additions & 0 deletions core/base/transaction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package base

type TransactionStatus = SDKEnumInt

const (
TransactionStatusNone TransactionStatus = 0
TransactionStatusPending TransactionStatus = 1
TransactionStatusSuccess TransactionStatus = 2
TransactionStatusFailure TransactionStatus = 3
)

type Transaction struct {
}

// Transaction details that can be fetched from the chain
type TransactionDetail struct {
// hash string on chain
HashString string

// transaction amount
Amount string

EstimateFees string

// sender's address
FromAddress string
// receiver's address
ToAddress string

Status TransactionStatus
// transaction completion timestamp, 0 if Status is in Pending
FinishTimestamp int64
// failure message
FailureMessage string
}
28 changes: 28 additions & 0 deletions core/base/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package base

import "sync"

type SDKEnumInt = int
type SDKEnumString = string

type safeMap struct {
sync.RWMutex
Map map[interface{}]interface{}
}

func newSafeMap() *safeMap {
return &safeMap{Map: make(map[interface{}]interface{})}
}

func (l *safeMap) readMap(key interface{}) (interface{}, bool) {
l.RLock()
value, ok := l.Map[key]
l.RUnlock()
return value, ok
}

func (l *safeMap) writeMap(key interface{}, value interface{}) {
l.Lock()
l.Map[key] = value
l.Unlock()
}
86 changes: 86 additions & 0 deletions core/base/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,94 @@ package base

import (
"errors"
"math/big"
"strconv"
"sync"
)

// This method will traverse the array concurrently and map each object in the array.
// @param list : [TYPE1], a list that all item is TYPE1
// @param maper : func(TYPE1) (TYPE2, error), a function that input TYPE1, return TYPE2
// you can throw an error to finish task.
// @return : [TYPE2], a list that all item is TYPE2
// @example : ```
// nums := []interface{}{1, 2, 3, 4, 5, 6}
// res, _ := MapListConcurrent(nums, func(i interface{}) (interface{}, error) {
// return strconv.Itoa(i.(int) * 100), nil
// })
// println(res) // ["100" "200" "300" "400" "500" "600"]
// ```
func MapListConcurrent(list []interface{}, maper func(interface{}) (interface{}, error)) ([]interface{}, error) {
thread := 0
max := 10
wg := sync.WaitGroup{}

mapContainer := newSafeMap()
var firstError error
for _, item := range list {
if firstError != nil {
continue
}
if thread == max {
wg.Wait()
thread = 0
}
if thread < max {
wg.Add(1)
}

go func(w *sync.WaitGroup, item interface{}, mapContainer *safeMap, firstError *error) {
maped, err := maper(item)
if *firstError == nil && err != nil {
*firstError = err
} else {
mapContainer.writeMap(item, maped)
}
wg.Done()
}(&wg, item, mapContainer, &firstError)
thread++
}
wg.Wait()
if firstError != nil {
return nil, firstError
}

result := []interface{}{}
for _, item := range list {
result = append(result, mapContainer.Map[item])
}
return result, nil
}

// The encapsulation of MapListConcurrent.
func MapListConcurrentStringToString(strList []string, maper func(string) (string, error)) ([]string, error) {
list := make([]interface{}, len(strList))
for i, s := range strList {
list[i] = s
}
temp, err := MapListConcurrent(list, func(i interface{}) (interface{}, error) {
return maper(i.(string))
})
if err != nil {
return nil, err
}

result := make([]string, len(temp))
for i, v := range temp {
result[i] = v.(string)
}
return result, nil
}

// Return the more biger of the two numbers
func MaxBigInt(x, y *big.Int) *big.Int {
if x.Cmp(y) > 0 {
return x
} else {
return y
}
}

/* [zh] 该方法会捕捉 panic 抛出的值,并转成一个 error 对象通过参数指针返回
* 注意: 如果想要返回它抓住的 error, 必须使用命名返回值!!
* [en] This method will catch the value thrown by panic, and turn it into an error object and return it through the parameter pointer
Expand All @@ -18,6 +103,7 @@ import (
* ```
*/
func CatchPanicAndMapToBasicError(errOfResult *error) {
// first we have to recover()
errOfPanic := recover()
if errOfResult == nil {
return
Expand Down
Loading

0 comments on commit 6b6dffb

Please sign in to comment.