diff --git a/pkg/client-sdk/client/rest/client.go b/pkg/client-sdk/client/rest/client.go index 99d7863..d02ee98 100644 --- a/pkg/client-sdk/client/rest/client.go +++ b/pkg/client-sdk/client/rest/client.go @@ -38,6 +38,7 @@ func NewClient(aspUrl string) (client.ASPClient, error) { if err != nil { return nil, err } + // TODO: use twice the round interval. reqTimeout := 15 * time.Second treeCache := utils.NewCache[tree.CongestionTree]() @@ -223,25 +224,19 @@ func (a *restClient) SubmitSignedForfeitTxs( func (a *restClient) GetEventStream( ctx context.Context, paymentID string, ) (<-chan client.RoundEventChannel, func(), error) { + ctx, cancel := context.WithTimeout(ctx, a.requestTimeout) eventsCh := make(chan client.RoundEventChannel) - stopCh := make(chan struct{}) - go func(payID string) { + go func(payID string, eventsCh chan client.RoundEventChannel) { + ticker := time.NewTicker(1 * time.Second) defer close(eventsCh) - defer close(stopCh) - - timeout := time.After(a.requestTimeout) + defer ticker.Stop() for { select { - case <-stopCh: + case <-ctx.Done(): return - case <-timeout: - eventsCh <- client.RoundEventChannel{ - Err: fmt.Errorf("timeout reached"), - } - return - default: + case <-ticker.C: event, err := a.Ping(ctx, payID) if err != nil { eventsCh <- client.RoundEventChannel{ @@ -255,17 +250,11 @@ func (a *restClient) GetEventStream( Event: event, } } - - time.Sleep(1 * time.Second) } } - }(paymentID) + }(paymentID, eventsCh) - close := func() { - stopCh <- struct{}{} - } - - return eventsCh, close, nil + return eventsCh, cancel, nil } func (a *restClient) Ping( diff --git a/pkg/client-sdk/example/covenantless/wasm/README.md b/pkg/client-sdk/example/covenantless/wasm/README.md index aec4d08..7b3d14d 100644 --- a/pkg/client-sdk/example/covenantless/wasm/README.md +++ b/pkg/client-sdk/example/covenantless/wasm/README.md @@ -2,20 +2,23 @@ This example demonstrates how to compile ARK Go SDK to WebAssembly and use it in a web page. -1. Create a Go file with the main package, check [main.go](main.go). - -2. Copy `wasm_exec.js`: +1. Copy `wasm_exec.js` to a new directory: ```bash cp $(go env GOROOT)/misc/wasm/wasm_exec.js . ``` -3. Build the Go code to WebAssembly: +2. On the root directory of this repo, build the Go code to WebAssembly: ```bash - GOOS=js GOARCH=wasm go build -o main.wasm main.go + make build-wasm ``` +3. Move the wasm file to your directory + + ```bash + mv /pkg/client-sdk/build/ark-sdk.wasm . + 4. Load the WebAssembly module in a web page, check [index.html](index.html). 5. Serve the files: diff --git a/pkg/client-sdk/example/covenantless/wasm/index.html b/pkg/client-sdk/example/covenantless/wasm/index.html index 14ae0a1..e1fb0c5 100644 --- a/pkg/client-sdk/example/covenantless/wasm/index.html +++ b/pkg/client-sdk/example/covenantless/wasm/index.html @@ -44,8 +44,7 @@ try { const addresses = await receive(); logMessage("Offchain address: " + addresses.offchainAddr); - logMessage("Onchain address: " + addresses.onchainAddr); - logMessage("If in regtest faucet onchain address: " + addresses.onchainAddr); + logMessage("Boarding address: " + addresses.boardingAddr); } catch (err) { logMessage("Receive error: " + err.message); } @@ -53,8 +52,10 @@ async function getBalance() { const bal = await balance(false); - logMessage("Onchain balance: " + bal.onchain_balance) - logMessage("Offchain balance: " + bal.offchain_balance) + logMessage("Offchain balance: " + bal.offchainBalance) + logMessage("Onchain balance: ") + logMessage(" Spendable: " + bal.onchainBalance.spendable) + logMessage(" Locked: " + bal.onchainBalance.locked) } @@ -88,6 +89,33 @@ } } + async function claimVtxos() { + const password = document.getElementById("c_password").value; + if (!password) { + logMessage("Claim error: password is required"); + return; + } + + try { + await unlock(password); + const txID = await claim(); + logMessage("Claimed money with tx ID: " + txID); + } catch (err) { + logMessage("Claim error: " + err.message); + } finally { + await lock(password); + } + } + + async function history() { + try { + const history = await getTransactionHistory(); + logMessage("Tx history: " + history); + } catch (err) { + logMessage("Tx history error: " + err.message); + } + } + async function config() { try { const aspUrl = await getAspUrl(); @@ -112,30 +140,6 @@ logMessage("Config error: " + err.message); } } - - async function board() { - const amountStr = document.getElementById("amount").value; - const amount = parseInt(amountStr, 10); - const password = document.getElementById("o_password").value; - if (!password) { - logMessage("Onboard error: password is required"); - return; - } - - try { - console.log("unlocking..."); - await unlock(password); - console.log(amount, password); - console.log("onboarding..."); - const txID = await onboard(amount); - logMessage("Onboarded with amount: " + amount + " and txID: " + txID + ", if in regtest mine a block"); - } catch (err) { - logMessage("Onboard error: " + err.message); - } finally { - await lock(password); - } - - } @@ -154,17 +158,19 @@
-
- - - -
+
+ + +
+
+ +
diff --git a/pkg/client-sdk/wasm/browser/exports.go b/pkg/client-sdk/wasm/browser/exports.go index 25ee785..6c8d3c1 100644 --- a/pkg/client-sdk/wasm/browser/exports.go +++ b/pkg/client-sdk/wasm/browser/exports.go @@ -30,8 +30,10 @@ func init() { js.Global().Set("receive", ReceiveWrapper()) js.Global().Set("sendOnChain", SendOnChainWrapper()) js.Global().Set("sendOffChain", SendOffChainWrapper()) + js.Global().Set("claim", ClaimWrapper()) js.Global().Set("unilateralRedeem", UnilateralRedeemWrapper()) js.Global().Set("collaborativeRedeem", CollaborativeRedeemWrapper()) + js.Global().Set("getTransactionHistory", GetTransactionHistoryWrapper()) js.Global().Set("log", LogWrapper()) js.Global().Set("getAspUrl", GetAspUrlWrapper()) diff --git a/pkg/client-sdk/wasm/browser/wrappers.go b/pkg/client-sdk/wasm/browser/wrappers.go index d5a17c1..752ed12 100644 --- a/pkg/client-sdk/wasm/browser/wrappers.go +++ b/pkg/client-sdk/wasm/browser/wrappers.go @@ -6,9 +6,12 @@ package browser import ( "context" "encoding/hex" + "encoding/json" "errors" "fmt" + "strconv" "syscall/js" + "time" arksdk "github.com/ark-network/ark/pkg/client-sdk" "github.com/ark-network/ark/pkg/client-sdk/wallet" @@ -119,21 +122,25 @@ func BalanceWrapper() js.Func { } var ( - onchainBalance int - offchainBalance int + onchainSpendableBalance int + onchainLockedBalance int + offchainBalance int ) - if resp == nil { - onchainBalance = 0 - offchainBalance = 0 - } else { - onchainBalance = int(resp.OnchainBalance.SpendableAmount) + if resp != nil { + onchainSpendableBalance = int(resp.OnchainBalance.SpendableAmount) + for _, b := range resp.OnchainBalance.LockedAmount { + onchainLockedBalance += int(b.Amount) + } offchainBalance = int(resp.OffchainBalance.Total) } result := map[string]interface{}{ - "onchain_balance": onchainBalance, - "offchain_balance": offchainBalance, + "onchainBalance": map[string]interface{}{ + "spendable": onchainSpendableBalance, + "locked": onchainLockedBalance, + }, + "offchainBalance": offchainBalance, } return js.ValueOf(result), nil @@ -204,6 +211,21 @@ func SendOffChainWrapper() js.Func { }) } +func ClaimWrapper() js.Func { + return JSPromise(func(args []js.Value) (interface{}, error) { + if len(args) != 0 { + return nil, errors.New("invalid number of args") + } + + resp, err := arkSdkClient.Claim(context.Background()) + if err != nil { + return nil, err + } + + return js.ValueOf(resp), nil + }) +} + func UnilateralRedeemWrapper() js.Func { return JSPromise(func(args []js.Value) (interface{}, error) { return nil, arkSdkClient.UnilateralRedeem(context.Background()) @@ -229,6 +251,32 @@ func CollaborativeRedeemWrapper() js.Func { }) } +func GetTransactionHistoryWrapper() js.Func { + return JSPromise(func(args []js.Value) (interface{}, error) { + history, err := arkSdkClient.GetTransactionHistory(context.Background()) + if err != nil { + return nil, err + } + rawHistory := make([]map[string]interface{}, 0) + for _, record := range history { + rawHistory = append(rawHistory, map[string]interface{}{ + "boardingTxid": record.BoardingTxid, + "roundTxid": record.RoundTxid, + "redeemTxid": record.RedeemTxid, + "amount": strconv.Itoa(int(record.Amount)), + "type": record.Type, + "isPending": record.IsPending, + "createdAt": record.CreatedAt.Format(time.RFC3339), + }) + } + result, err := json.MarshalIndent(rawHistory, "", " ") + if err != nil { + return nil, err + } + return js.ValueOf(string(result)), nil + }) +} + func GetAspUrlWrapper() js.Func { return js.FuncOf(func(this js.Value, p []js.Value) interface{} { data, _ := arkSdkClient.GetConfigData(context.Background()) diff --git a/server/internal/core/application/covenant.go b/server/internal/core/application/covenant.go index 7033ffe..0cbc140 100644 --- a/server/internal/core/application/covenant.go +++ b/server/internal/core/application/covenant.go @@ -421,7 +421,6 @@ func (s *covenantService) startRound() { round := domain.NewRound(dustAmount) //nolint:all round.StartRegistration() - s.lastEvent = nil s.currentRound = round defer func() { diff --git a/server/internal/core/application/covenantless.go b/server/internal/core/application/covenantless.go index 801db28..384d414 100644 --- a/server/internal/core/application/covenantless.go +++ b/server/internal/core/application/covenantless.go @@ -712,7 +712,6 @@ func (s *covenantlessService) startRound() { round := domain.NewRound(dustAmount) //nolint:all round.StartRegistration() - s.lastEvent = nil s.currentRound = round defer func() {