Skip to content

Commit 6fac1e2

Browse files
authored
feat(ts): client (#1)
1 parent 673eaf2 commit 6fac1e2

26 files changed

+9997
-23
lines changed

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ cache/
44
artifacts/
55
logs
66
*.log
7-
7+
go/vendor
8+
go/imports
9+
go/build
810
## CVE-2021-21300 style exploits
911
.\#*
1012

.prettierrc.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"$schema": "http://json.schemastore.org/prettierrc",
3+
"arrowParens": "always",
4+
"bracketSpacing": true,
5+
"jsxBracketSameLine": false,
6+
"jsxSingleQuote": false,
7+
"printWidth": 80,
8+
"proseWrap": "always",
9+
"quoteProps": "as-needed",
10+
"semi": true,
11+
"singleQuote": true,
12+
"tabWidth": 2,
13+
"trailingComma": "all",
14+
"endOfLine": "lf",
15+
"useTabs": false
16+
}

codecov.yml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
coverage:
2+
status:
3+
project:
4+
default:
5+
branches: [master]
6+
informational: true
7+
patch:
8+
default:
9+
only_pulls: true # No patch status on single commits in master
10+
informational: true

fns/getTransactionReceiptMined.ts

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
2+
export function getTransactionReceiptMined(txHash: string | any[], interval: number) {
3+
const self = this;
4+
const transactionReceiptAsync = (resolve: (arg0: any) => void, reject: (arg0: any) => void) => {
5+
self.getTransactionReceipt(txHash, (error: any, receipt: any) => {
6+
if (error) {
7+
reject(error);
8+
} else if (receipt == null) {
9+
setTimeout(
10+
() => transactionReceiptAsync(resolve, reject),
11+
interval ? interval : 500);
12+
} else {
13+
resolve(receipt);
14+
}
15+
});
16+
};
17+
18+
if (Array.isArray(txHash)) {
19+
return Promise.all(txHash.map(
20+
oneTxHash => self.getTransactionReceiptMined(oneTxHash, interval)));
21+
} else if (typeof txHash === "string") {
22+
return new Promise(transactionReceiptAsync);
23+
} else {
24+
throw new Error(`Invalid Type: ${txHash}`);
25+
}
26+
};

go/.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
build
2+
vendor
3+
.DS_Store
4+

go/build.sh

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env bash
2+
export CGO_ENABLED=0
3+
export GO111MODULE=on
4+
go build -buildmode c-shared -o build/dgeth.dylib \
5+
-ldflags "-w -extldflags \"-static\" -X \"main.version=0.1.0\""
6+
github.com/sambacha/dgeth/main

go/chain/connection.go

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package chain
2+
3+
import (
4+
"context"
5+
"math/big"
6+
7+
"github.com/ethereum/go-ethereum/accounts/abi/bind"
8+
"github.com/ethereum/go-ethereum/common"
9+
"github.com/ethereum/go-ethereum/core/types"
10+
)
11+
12+
type ReceiptProvider interface {
13+
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
14+
15+
// Force a block creation event if running in simulator
16+
Commit()
17+
}
18+
19+
type Backend interface {
20+
bind.ContractBackend
21+
ReceiptProvider
22+
TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error)
23+
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
24+
}

go/check-list.sh

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/env bash
2+
go list -m -json all

go/go.mod

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
module github.com/sambacha/dgeth
2+
3+
go 1.18
4+
5+
require (
6+
github.com/ethereum/go-ethereum v1.10.18
7+
github.com/holiman/uint256 v1.2.0
8+
github.com/pkg/errors v0.9.1
9+
)
10+
11+
require (
12+
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
13+
github.com/VictoriaMetrics/fastcache v1.6.0 // indirect
14+
github.com/btcsuite/btcd v0.20.1-beta // indirect
15+
github.com/cespare/xxhash/v2 v2.1.1 // indirect
16+
github.com/deckarep/golang-set v1.8.0 // indirect
17+
github.com/edsrzf/mmap-go v1.0.0 // indirect
18+
github.com/go-ole/go-ole v1.2.1 // indirect
19+
github.com/go-stack/stack v1.8.0 // indirect
20+
github.com/golang/snappy v0.0.4 // indirect
21+
github.com/google/uuid v1.2.0 // indirect
22+
github.com/gorilla/websocket v1.4.2 // indirect
23+
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
24+
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
25+
github.com/mattn/go-runewidth v0.0.9 // indirect
26+
github.com/olekukonko/tablewriter v0.0.5 // indirect
27+
github.com/prometheus/tsdb v0.7.1 // indirect
28+
github.com/rjeczalik/notify v0.9.1 // indirect
29+
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
30+
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
31+
github.com/tklauser/go-sysconf v0.3.5 // indirect
32+
github.com/tklauser/numcpus v0.2.2 // indirect
33+
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
34+
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect
35+
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
36+
)

go/go.sum

+655
Large diffs are not rendered by default.

go/main/main.go

+250
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"encoding/hex"
6+
"encoding/json"
7+
"fmt"
8+
"log"
9+
"math/big"
10+
"math/rand"
11+
"strconv"
12+
"time"
13+
14+
"C"
15+
16+
"github.com/ethereum/go-ethereum"
17+
"github.com/ethereum/go-ethereum/common"
18+
"github.com/ethereum/go-ethereum/core/types"
19+
"github.com/sambacha/dgeth/simulator"
20+
)
21+
22+
type TransactionRequest struct {
23+
To *string `json:"to"`
24+
From *string `json:"from"`
25+
Nonce *string `json:"nonce"`
26+
27+
GasLimit *string `json:"gasLimit"`
28+
GasPrice *string `json:"gasPrice"`
29+
30+
Data *string `json:"data"`
31+
Value *string `json:"value"`
32+
ChainId *uint64 `json:"chainId"`
33+
}
34+
35+
func main() {}
36+
37+
var (
38+
simulators = make(map[int]*simulator.Simulator)
39+
nextSimulatorID = 0
40+
)
41+
42+
//export newSimulator
43+
func newSimulator() C.int {
44+
sim, err := simulator.NewSimulator()
45+
if err != nil {
46+
log.Fatal(err)
47+
}
48+
id := nextSimulatorID
49+
simulators[id] = sim
50+
nextSimulatorID++
51+
return C.int(id)
52+
}
53+
54+
//export getBlockNumber
55+
func getBlockNumber(simID C.int) *C.char {
56+
sim := getSimulator(simID)
57+
bn := sim.GetLatestBlockNumber()
58+
return C.CString(bn.String())
59+
}
60+
61+
//export getCode
62+
func getCode(simID C.int, account *C.char) *C.char {
63+
sim := getSimulator(simID)
64+
code, err := sim.Backend.CodeAt(context.Background(), common.HexToAddress(C.GoString(account)), nil)
65+
if err != nil {
66+
log.Fatal(err)
67+
}
68+
69+
return C.CString(common.Bytes2Hex(code))
70+
}
71+
72+
//export getChainID
73+
func getChainID(simID C.int) *C.char {
74+
sim := getSimulator(simID)
75+
bn := sim.GetChainID()
76+
return C.CString(bn.String())
77+
}
78+
79+
//export getBalance
80+
func getBalance(simID C.int, account *C.char) *C.char {
81+
sim := getSimulator(simID)
82+
bal, err := sim.Backend.BalanceAt(context.Background(), common.HexToAddress(C.GoString(account)), nil)
83+
if err != nil {
84+
log.Fatal(err)
85+
}
86+
87+
return C.CString(bal.String())
88+
}
89+
90+
//export getTransactionCount
91+
func getTransactionCount(simID C.int, account *C.char) C.int {
92+
sim := getSimulator(simID)
93+
count, err := sim.Backend.NonceAt(context.Background(), common.HexToAddress(C.GoString(account)), nil)
94+
if err != nil {
95+
log.Fatal(err)
96+
}
97+
98+
return C.int(count)
99+
}
100+
101+
//export getLogs
102+
func getLogs(simID C.int, queryJson *C.char) *C.char {
103+
sim := getSimulator(simID)
104+
105+
var query ethereum.FilterQuery
106+
107+
err := json.Unmarshal([]byte(C.GoString(queryJson)), &query)
108+
if err != nil {
109+
log.Fatal(err)
110+
}
111+
112+
logs, err := sim.Backend.FilterLogs(context.Background(), query)
113+
114+
if err != nil {
115+
log.Fatal(err)
116+
}
117+
118+
logsJson, err := json.Marshal(logs)
119+
return C.CString(string(logsJson))
120+
}
121+
122+
//export call
123+
func call(simID C.int, msgJson *C.char) *C.char {
124+
sim := getSimulator(simID)
125+
var msg TransactionRequest
126+
127+
err := json.Unmarshal([]byte(C.GoString(msgJson)), &msg)
128+
if err != nil {
129+
log.Fatal(err)
130+
}
131+
132+
var callMsg ethereum.CallMsg
133+
134+
if msg.From != nil {
135+
callMsg.From = common.HexToAddress(*msg.From)
136+
}
137+
if msg.To != nil {
138+
temp := common.HexToAddress(*msg.To)
139+
callMsg.To = &temp
140+
}
141+
if msg.GasLimit != nil {
142+
value, err := strconv.ParseUint(*msg.GasLimit, 16, 64)
143+
if err != nil {
144+
log.Fatal(err)
145+
}
146+
147+
callMsg.Gas = value
148+
}
149+
if msg.GasPrice != nil {
150+
callMsg.GasPrice = big.NewInt(0)
151+
callMsg.GasPrice.SetString(*msg.GasPrice, 16)
152+
}
153+
if msg.Data != nil {
154+
data, err := hex.DecodeString((*msg.Data)[2:])
155+
if err != nil {
156+
log.Fatal(err)
157+
}
158+
159+
callMsg.Data = data
160+
}
161+
162+
res, err := sim.Backend.CallContract(context.Background(), callMsg, nil)
163+
if err != nil {
164+
log.Fatal(err)
165+
}
166+
167+
return C.CString(hex.EncodeToString(res))
168+
}
169+
170+
type txReceipt struct {
171+
Tx *types.Transaction
172+
IsPending bool
173+
}
174+
175+
//export getTransaction
176+
func getTransaction(simID C.int, txHash *C.char) *C.char {
177+
sim := getSimulator(simID)
178+
179+
hash := common.HexToHash(C.GoString(txHash))
180+
tx, isPending, err := sim.Backend.SimulatedBackend.TransactionByHash(context.Background(), hash)
181+
if err != nil {
182+
log.Fatal(err)
183+
}
184+
185+
stringified, err := json.Marshal(txReceipt{Tx: tx, IsPending: isPending})
186+
if err != nil {
187+
log.Fatal(err)
188+
}
189+
190+
return C.CString(string(stringified[:]))
191+
}
192+
193+
//export sendTransaction
194+
func sendTransaction(simID C.int, txData *C.char) *C.char {
195+
sim := getSimulator(simID)
196+
197+
bytes, err := hex.DecodeString(C.GoString(txData)[2:])
198+
if err != nil {
199+
log.Fatal(err)
200+
}
201+
202+
tx := &types.Transaction{}
203+
err = tx.UnmarshalBinary(bytes)
204+
if err != nil {
205+
log.Fatal(err)
206+
}
207+
208+
err = sim.Backend.SimulatedBackend.SendTransaction(context.Background(), tx)
209+
if err != nil {
210+
log.Fatal(err)
211+
}
212+
213+
sim.Backend.Commit()
214+
215+
receipt, err := sim.Backend.SimulatedBackend.TransactionReceipt(context.Background(), tx.Hash())
216+
if err != nil {
217+
log.Fatal(err)
218+
}
219+
220+
receiptJson, err := json.Marshal(receipt)
221+
if err != nil {
222+
log.Fatal(err)
223+
}
224+
225+
return C.CString(string(receiptJson))
226+
}
227+
228+
func getSimulator(simID C.int) *simulator.Simulator {
229+
id := int(simID)
230+
sim, ok := simulators[id]
231+
if !ok {
232+
log.Fatal(fmt.Errorf("simulator with %d does not exist", id))
233+
}
234+
return sim
235+
}
236+
237+
//export cgoCurrentMillis
238+
func cgoCurrentMillis() C.long {
239+
return C.long(time.Now().Unix())
240+
}
241+
242+
//export cgoSeed
243+
func cgoSeed(m C.long) {
244+
rand.Seed(int64(m))
245+
}
246+
247+
//export cgoRandom
248+
func cgoRandom(m C.int) C.int {
249+
return C.int(rand.Intn(int(m)))
250+
}

0 commit comments

Comments
 (0)