-
Notifications
You must be signed in to change notification settings - Fork 61
Pipeline + Go, Wasm, Java, Rust examples #65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
d5441fa
2941879
f007271
4ffcb4e
4b75b9a
1c53575
0b5b57f
0fe9475
92b7a90
d00ee6a
a2aaf3d
754026f
e79da33
30fc54d
7d0cf00
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| name: CI | ||
|
|
||
| on: | ||
| push: | ||
| pull_request: | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| build-and-test: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - uses: actions/setup-go@v5 | ||
| with: | ||
| go-version: '1.23.1' | ||
|
alexvelea marked this conversation as resolved.
Outdated
|
||
| cache: true | ||
|
|
||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 22 | ||
|
|
||
| - uses: actions/setup-java@v4 | ||
| with: | ||
| distribution: temurin | ||
| java-version: '21' | ||
| cache: maven | ||
|
|
||
| - uses: dtolnay/rust-toolchain@stable | ||
|
|
||
| - uses: Swatinem/rust-cache@v2 | ||
| with: | ||
| workspaces: examples/rust | ||
|
|
||
| - uses: extractions/setup-just@v2 | ||
|
|
||
| - run: go test ./... | ||
|
|
||
| - run: just build-linux-local | ||
|
|
||
| - run: just build-wasm | ||
|
|
||
| - name: Install wasm_exec.js | ||
| run: | | ||
| SRC="$(go env GOROOT)/lib/wasm/wasm_exec.js" | ||
| [ -f "$SRC" ] || SRC="$(go env GOROOT)/misc/wasm/wasm_exec.js" | ||
| cp "$SRC" ./build/wasm_exec.js | ||
|
|
||
| - name: Run WASM example | ||
| run: node ./examples/wasm/test_wasm.mjs | ||
|
|
||
| - run: just build-java | ||
|
|
||
| - run: just build-rust | ||
|
|
||
| - uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: lighter-go-linux-wasm | ||
| path: | | ||
| build/lighter-signer-linux.so | ||
| build/lighter-signer-linux.h | ||
| build/lighter-signer.wasm | ||
| build/wasm_exec.js | ||
|
|
||
| build-darwin: | ||
| runs-on: macos-14 | ||
|
zen marked this conversation as resolved.
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - uses: actions/setup-go@v5 | ||
| with: | ||
| go-version-file: go.mod | ||
| cache: true | ||
|
|
||
| - uses: extractions/setup-just@v2 | ||
|
|
||
| - run: just build-darwin-local | ||
|
|
||
| - name: Compile C++ example | ||
| run: clang++ -std=c++20 -O3 ./examples/cpp/example.cpp ./build/lighter-signer-darwin-arm64.dylib -o ./build/example-cpp | ||
|
alexvelea marked this conversation as resolved.
|
||
|
|
||
| - uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: lighter-go-darwin | ||
| path: build/lighter-signer-darwin-arm64.dylib | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We also would need: concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: trueto prevent multiple pushes creating queue Also for all external actions we need to pin them to SHA instead of tag (tags are mutable and prone to suplply chain attacks) .e.g. uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2All actions with SHAs: No release workflow right now. CI uploads artifacts, but nothing creates a GitHub Release on tag or attaches the binaries. Looks like the existing releases were done manually — might be worth automating at some point. darwin/amd64 (Intel Mac) isn’t built in CI. macos-14 only covers arm64, so if you’re still shipping Intel dylibs, they’re not being verified. Artifact retention isn’t explicitly set (so it defaults to 90 days). That’s fine for now, but could be worth setting it explicitly. Running Java and Rust in the same Ubuntu job adds quite a bit of setup time. If builds start getting slow, it might make sense to split them into separate jobs. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,282 @@ | ||
| package client | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/elliottech/lighter-go/types" | ||
| "github.com/elliottech/lighter-go/types/txtypes" | ||
| ) | ||
|
|
||
| const ( | ||
| testChainID uint32 = 304 | ||
| testAccountIndex int64 = 1 | ||
| testAPIKeyIndex uint8 = 0 | ||
| testNonce int64 = 42 | ||
| ) | ||
|
|
||
| func newTestClient(t *testing.T, privateKey string) *TxClient { | ||
| t.Helper() | ||
| c, err := NewTxClient(nil, privateKey, testAccountIndex, testAPIKeyIndex, testChainID) | ||
| if err != nil { | ||
| t.Fatalf("NewTxClient failed: %v", err) | ||
| } | ||
| return c | ||
| } | ||
|
|
||
| func opsWithSkipNonce(skipNonce uint8, nonce int64) *types.TransactOpts { | ||
| attr := &types.L2TxAttributes{} | ||
| if skipNonce == 1 { | ||
| sn := skipNonce | ||
| attr.SkipNonce = &sn | ||
| } | ||
| n := nonce | ||
| return &types.TransactOpts{ | ||
| Nonce: &n, | ||
| TxAttributes: attr, | ||
| } | ||
| } | ||
|
|
||
| func assertSkipNonce(t *testing.T, name string, attrs txtypes.L2TxAttributes, expectedSet bool) { | ||
| t.Helper() | ||
| if expectedSet { | ||
| if attrs == nil { | ||
| t.Errorf("%s: attributes map should be non-nil when skipNonce=1", name) | ||
| return | ||
| } | ||
| att, ok := attrs[txtypes.AttributeTypeSkipTxNonce] | ||
| if !ok { | ||
| t.Errorf("%s: SkipTxNonce key missing", name) | ||
| return | ||
| } | ||
| if att != 1 { | ||
| t.Errorf("%s: SkipTxNonce = %d, want 1", name, att) | ||
| } | ||
| return | ||
| } | ||
| if attrs != nil { | ||
| t.Errorf("%s: attributes map should be nil when skipNonce=0, got %v", name, attrs) | ||
| } | ||
| } | ||
|
|
||
| func TestGenerateAPIKey(t *testing.T) { | ||
| priv, pub, err := GenerateAPIKey() | ||
| if err != nil { | ||
| t.Fatalf("GenerateAPIKey error: %v", err) | ||
| } | ||
| if len(priv) < 3 || priv[:2] != "0x" { | ||
| t.Errorf("privateKey should be a 0x-prefixed hex string, got %q", priv) | ||
| } | ||
| if len(pub) < 3 || pub[:2] != "0x" { | ||
| t.Errorf("publicKey should be a 0x-prefixed hex string, got %q", pub) | ||
| } | ||
| } | ||
|
|
||
| func TestCreateClient(t *testing.T) { | ||
| priv, _, err := GenerateAPIKey() | ||
| if err != nil { | ||
| t.Fatalf("GenerateAPIKey error: %v", err) | ||
| } | ||
| c := newTestClient(t, priv) | ||
| if c.GetChainId() != testChainID { | ||
| t.Errorf("chainId = %d, want %d", c.GetChainId(), testChainID) | ||
| } | ||
| if c.GetAccountIndex() != testAccountIndex { | ||
| t.Errorf("accountIndex = %d, want %d", c.GetAccountIndex(), testAccountIndex) | ||
| } | ||
| if c.GetApiKeyIndex() != testAPIKeyIndex { | ||
| t.Errorf("apiKeyIndex = %d, want %d", c.GetApiKeyIndex(), testAPIKeyIndex) | ||
| } | ||
| } | ||
|
|
||
| func TestSignCancelOrder(t *testing.T) { | ||
| priv, _, _ := GenerateAPIKey() | ||
|
alexvelea marked this conversation as resolved.
Outdated
|
||
| c := newTestClient(t, priv) | ||
| req := &types.CancelOrderTxReq{MarketIndex: 0, Index: 12345} | ||
|
|
||
| // skipNonce = 1 | ||
| tx1, err := c.GetCancelOrderTransaction(req, opsWithSkipNonce(1, testNonce)) | ||
| if err != nil { | ||
| t.Fatalf("GetCancelOrderTransaction (skipNonce=1) failed: %v", err) | ||
| } | ||
| if tx1.GetTxType() != txtypes.TxTypeL2CancelOrder { | ||
| t.Errorf("txType = %d, want %d", tx1.GetTxType(), txtypes.TxTypeL2CancelOrder) | ||
| } | ||
| if tx1.AccountIndex != testAccountIndex { | ||
| t.Errorf("AccountIndex = %d, want %d", tx1.AccountIndex, testAccountIndex) | ||
| } | ||
| if tx1.ApiKeyIndex != testAPIKeyIndex { | ||
| t.Errorf("ApiKeyIndex = %d, want %d", tx1.ApiKeyIndex, testAPIKeyIndex) | ||
| } | ||
| if tx1.MarketIndex != 0 { | ||
| t.Errorf("MarketIndex = %d, want 0", tx1.MarketIndex) | ||
| } | ||
| if tx1.Index != 12345 { | ||
| t.Errorf("Index = %d, want 12345", tx1.Index) | ||
| } | ||
| if tx1.Nonce != testNonce { | ||
| t.Errorf("Nonce = %d, want %d", tx1.Nonce, testNonce) | ||
| } | ||
| if len(tx1.Sig) == 0 { | ||
| t.Error("Sig should not be empty") | ||
| } | ||
| assertSkipNonce(t, "SignCancelOrder(skipNonce=1)", tx1.L2TxAttributes, true) | ||
|
|
||
| tx2, err := c.GetCancelOrderTransaction(req, opsWithSkipNonce(0, testNonce)) | ||
| if err != nil { | ||
| t.Fatalf("GetCancelOrderTransaction (skipNonce=0) failed: %v", err) | ||
| } | ||
| assertSkipNonce(t, "SignCancelOrder(skipNonce=0)", tx2.L2TxAttributes, false) | ||
|
|
||
| if tx1.SignedHash == tx2.SignedHash { | ||
| t.Error("skipNonce flag should affect the signed tx hash, but hashes are identical") | ||
| } | ||
| } | ||
|
|
||
| func TestSignCancelAllOrders(t *testing.T) { | ||
| priv, _, _ := GenerateAPIKey() | ||
| c := newTestClient(t, priv) | ||
| req := &types.CancelAllOrdersTxReq{TimeInForce: 0, Time: 0} | ||
|
|
||
| tx, err := c.GetCancelAllOrdersTransaction(req, opsWithSkipNonce(1, testNonce)) | ||
| if err != nil { | ||
| t.Fatalf("GetCancelAllOrdersTransaction failed: %v", err) | ||
| } | ||
| if tx.GetTxType() != txtypes.TxTypeL2CancelAllOrders { | ||
| t.Errorf("txType = %d, want %d", tx.GetTxType(), txtypes.TxTypeL2CancelAllOrders) | ||
| } | ||
| if tx.TimeInForce != 0 { | ||
| t.Errorf("TimeInForce = %d, want 0", tx.TimeInForce) | ||
| } | ||
| if tx.Time != 0 { | ||
| t.Errorf("Time = %d, want 0", tx.Time) | ||
| } | ||
| assertSkipNonce(t, "SignCancelAllOrders(skipNonce=1)", tx.L2TxAttributes, true) | ||
| } | ||
|
|
||
| func TestSignCreateOrder(t *testing.T) { | ||
| priv, _, _ := GenerateAPIKey() | ||
| c := newTestClient(t, priv) | ||
|
|
||
| req := &types.CreateOrderTxReq{ | ||
| MarketIndex: 0, | ||
| ClientOrderIndex: 1, | ||
| BaseAmount: 1000, | ||
| Price: 50000, | ||
| IsAsk: 0, | ||
| Type: 0, | ||
| TimeInForce: 0, | ||
| ReduceOnly: 0, | ||
| TriggerPrice: 0, | ||
| OrderExpiry: 0, | ||
| } | ||
| tx, err := c.GetCreateOrderTransaction(req, opsWithSkipNonce(1, testNonce)) | ||
| if err != nil { | ||
| t.Fatalf("GetCreateOrderTransaction failed: %v", err) | ||
| } | ||
| if tx.GetTxType() != txtypes.TxTypeL2CreateOrder { | ||
| t.Errorf("txType = %d, want %d", tx.GetTxType(), txtypes.TxTypeL2CreateOrder) | ||
| } | ||
| if tx.MarketIndex != 0 { | ||
| t.Errorf("MarketIndex = %d, want 0", tx.MarketIndex) | ||
| } | ||
| if tx.ClientOrderIndex != 1 { | ||
| t.Errorf("ClientOrderIndex = %d, want 1", tx.ClientOrderIndex) | ||
| } | ||
| if tx.BaseAmount != 1000 { | ||
| t.Errorf("BaseAmount = %d, want 1000", tx.BaseAmount) | ||
| } | ||
| if tx.Price != 50000 { | ||
| t.Errorf("Price = %d, want 50000", tx.Price) | ||
| } | ||
| assertSkipNonce(t, "SignCreateOrder(skipNonce=1)", tx.L2TxAttributes, true) | ||
| } | ||
|
|
||
| func TestSignCreateSubAccount(t *testing.T) { | ||
| priv, _, _ := GenerateAPIKey() | ||
| c := newTestClient(t, priv) | ||
|
|
||
| tx, err := c.GetCreateSubAccountTransaction(opsWithSkipNonce(1, testNonce)) | ||
| if err != nil { | ||
| t.Fatalf("GetCreateSubAccountTransaction failed: %v", err) | ||
| } | ||
| if tx.GetTxType() != txtypes.TxTypeL2CreateSubAccount { | ||
| t.Errorf("txType = %d, want %d", tx.GetTxType(), txtypes.TxTypeL2CreateSubAccount) | ||
| } | ||
| if tx.AccountIndex != testAccountIndex { | ||
| t.Errorf("AccountIndex = %d, want %d", tx.AccountIndex, testAccountIndex) | ||
| } | ||
| if tx.Nonce != testNonce { | ||
| t.Errorf("Nonce = %d, want %d", tx.Nonce, testNonce) | ||
| } | ||
| assertSkipNonce(t, "SignCreateSubAccount(skipNonce=1)", tx.L2TxAttributes, true) | ||
| } | ||
|
|
||
| func TestSignUpdateLeverage(t *testing.T) { | ||
| priv, _, _ := GenerateAPIKey() | ||
| c := newTestClient(t, priv) | ||
|
|
||
| req := &types.UpdateLeverageTxReq{ | ||
| MarketIndex: 0, | ||
| InitialMarginFraction: 100, | ||
| MarginMode: 0, | ||
| } | ||
| tx, err := c.GetUpdateLeverageTransaction(req, opsWithSkipNonce(1, testNonce)) | ||
| if err != nil { | ||
| t.Fatalf("GetUpdateLeverageTransaction failed: %v", err) | ||
| } | ||
| if tx.GetTxType() != txtypes.TxTypeL2UpdateLeverage { | ||
| t.Errorf("txType = %d, want %d", tx.GetTxType(), txtypes.TxTypeL2UpdateLeverage) | ||
| } | ||
| if tx.MarketIndex != 0 { | ||
| t.Errorf("MarketIndex = %d, want 0", tx.MarketIndex) | ||
| } | ||
| if tx.InitialMarginFraction != 100 { | ||
| t.Errorf("InitialMarginFraction = %d, want 100", tx.InitialMarginFraction) | ||
| } | ||
| assertSkipNonce(t, "SignUpdateLeverage(skipNonce=1)", tx.L2TxAttributes, true) | ||
| } | ||
|
|
||
| func TestSignCreateGroupedOrders(t *testing.T) { | ||
| priv, _, _ := GenerateAPIKey() | ||
| c := newTestClient(t, priv) | ||
|
|
||
| const expiry int64 = 7 * 24 * 60 * 60 * 1000 | ||
|
|
||
| req := &types.CreateGroupedOrdersTxReq{ | ||
| GroupingType: 1, | ||
| Orders: []*types.CreateOrderTxReq{ | ||
| { | ||
| MarketIndex: 0, ClientOrderIndex: 0, BaseAmount: 1000, Price: 50000, | ||
| IsAsk: 0, Type: 0, TimeInForce: 1, ReduceOnly: 0, TriggerPrice: 0, OrderExpiry: expiry, | ||
| }, | ||
| { | ||
| MarketIndex: 0, ClientOrderIndex: 0, BaseAmount: 0, Price: 51000, | ||
| IsAsk: 1, Type: 4, TimeInForce: 0, ReduceOnly: 1, TriggerPrice: 49000, OrderExpiry: expiry, | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
| tx, err := c.GetCreateGroupedOrdersTransaction(req, opsWithSkipNonce(1, testNonce)) | ||
| if err != nil { | ||
| t.Fatalf("GetCreateGroupedOrdersTransaction failed: %v", err) | ||
| } | ||
| if tx.GetTxType() != txtypes.TxTypeL2CreateGroupedOrders { | ||
| t.Errorf("txType = %d, want %d", tx.GetTxType(), txtypes.TxTypeL2CreateGroupedOrders) | ||
| } | ||
| if tx.GroupingType != 1 { | ||
| t.Errorf("GroupingType = %d, want 1", tx.GroupingType) | ||
| } | ||
| if len(tx.Orders) != 2 { | ||
| t.Fatalf("Orders len = %d, want 2", len(tx.Orders)) | ||
| } | ||
| if tx.Orders[0].TimeInForce != 1 { | ||
| t.Errorf("Orders[0].TimeInForce = %d, want 1", tx.Orders[0].TimeInForce) | ||
| } | ||
| if tx.Orders[1].Type != 4 { | ||
| t.Errorf("Orders[1].Type = %d, want 4", tx.Orders[1].Type) | ||
| } | ||
| if tx.Orders[1].TriggerPrice != 49000 { | ||
| t.Errorf("Orders[1].TriggerPrice = %d, want 49000", tx.Orders[1].TriggerPrice) | ||
| } | ||
| assertSkipNonce(t, "SignCreateGroupedOrders(skipNonce=1)", tx.L2TxAttributes, true) | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.