[SDK] Fix rest client and wasm (#341)

* Fixes

Co-authored-by: João Bordalo <bordalix@users.noreply.github.com>

* Fixes to rest client

* Fixes to wasm

---------

Co-authored-by: João Bordalo <bordalix@users.noreply.github.com>
This commit is contained in:
Pietralberto Mazza
2024-10-01 19:00:25 +02:00
committed by GitHub
parent a3deb2d596
commit 26bcbc8163
7 changed files with 115 additions and 69 deletions

View File

@@ -38,6 +38,7 @@ func NewClient(aspUrl string) (client.ASPClient, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// TODO: use twice the round interval.
reqTimeout := 15 * time.Second reqTimeout := 15 * time.Second
treeCache := utils.NewCache[tree.CongestionTree]() treeCache := utils.NewCache[tree.CongestionTree]()
@@ -223,25 +224,19 @@ func (a *restClient) SubmitSignedForfeitTxs(
func (a *restClient) GetEventStream( func (a *restClient) GetEventStream(
ctx context.Context, paymentID string, ctx context.Context, paymentID string,
) (<-chan client.RoundEventChannel, func(), error) { ) (<-chan client.RoundEventChannel, func(), error) {
ctx, cancel := context.WithTimeout(ctx, a.requestTimeout)
eventsCh := make(chan client.RoundEventChannel) 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(eventsCh)
defer close(stopCh) defer ticker.Stop()
timeout := time.After(a.requestTimeout)
for { for {
select { select {
case <-stopCh: case <-ctx.Done():
return return
case <-timeout: case <-ticker.C:
eventsCh <- client.RoundEventChannel{
Err: fmt.Errorf("timeout reached"),
}
return
default:
event, err := a.Ping(ctx, payID) event, err := a.Ping(ctx, payID)
if err != nil { if err != nil {
eventsCh <- client.RoundEventChannel{ eventsCh <- client.RoundEventChannel{
@@ -255,17 +250,11 @@ func (a *restClient) GetEventStream(
Event: event, Event: event,
} }
} }
time.Sleep(1 * time.Second)
} }
} }
}(paymentID) }(paymentID, eventsCh)
close := func() { return eventsCh, cancel, nil
stopCh <- struct{}{}
}
return eventsCh, close, nil
} }
func (a *restClient) Ping( func (a *restClient) Ping(

View File

@@ -2,20 +2,23 @@
This example demonstrates how to compile ARK Go SDK to WebAssembly and use it in a web page. 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). 1. Copy `wasm_exec.js` to a new directory:
2. Copy `wasm_exec.js`:
```bash ```bash
cp $(go env GOROOT)/misc/wasm/wasm_exec.js . 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 ```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 <repo>/pkg/client-sdk/build/ark-sdk.wasm .
4. Load the WebAssembly module in a web page, check [index.html](index.html). 4. Load the WebAssembly module in a web page, check [index.html](index.html).
5. Serve the files: 5. Serve the files:

View File

@@ -44,8 +44,7 @@
try { try {
const addresses = await receive(); const addresses = await receive();
logMessage("Offchain address: " + addresses.offchainAddr); logMessage("Offchain address: " + addresses.offchainAddr);
logMessage("Onchain address: " + addresses.onchainAddr); logMessage("Boarding address: " + addresses.boardingAddr);
logMessage("If in regtest faucet onchain address: " + addresses.onchainAddr);
} catch (err) { } catch (err) {
logMessage("Receive error: " + err.message); logMessage("Receive error: " + err.message);
} }
@@ -53,8 +52,10 @@
async function getBalance() { async function getBalance() {
const bal = await balance(false); const bal = await balance(false);
logMessage("Onchain balance: " + bal.onchain_balance) logMessage("Offchain balance: " + bal.offchainBalance)
logMessage("Offchain balance: " + bal.offchain_balance) 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() { async function config() {
try { try {
const aspUrl = await getAspUrl(); const aspUrl = await getAspUrl();
@@ -112,30 +140,6 @@
logMessage("Config error: " + err.message); 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);
}
}
</script> </script>
</head> </head>
<body> <body>
@@ -154,17 +158,19 @@
<div> <div>
<button onclick="getBalance()">Balance</button> <button onclick="getBalance()">Balance</button>
</div> </div>
<div>
<button onclick="board()">Onboard</button>
<input type="text" id="amount" placeholder="Amount">
<input type="password" id="o_password" placeholder="password">
</div>
<div> <div>
<button onclick="send()">Send</button> <button onclick="send()">Send</button>
<input type="text" id="sendAddress" placeholder="Offchain Address"> <input type="text" id="sendAddress" placeholder="Offchain Address">
<input type="text" id="amountToSend" placeholder="Amount"> <input type="text" id="amountToSend" placeholder="Amount">
<input type="password" id="s_password" placeholder="password"> <input type="password" id="s_password" placeholder="password">
</div> </div>
<div>
<button onclick="claimVtxos()">Claim</button>
<input type="password" id="c_password" placeholder="password">
</div>
<div>
<button onclick="history()">History</button>
</div>
<div> <div>
<button onclick="config()">Config</button> <button onclick="config()">Config</button>
</div> </div>

View File

@@ -30,8 +30,10 @@ func init() {
js.Global().Set("receive", ReceiveWrapper()) js.Global().Set("receive", ReceiveWrapper())
js.Global().Set("sendOnChain", SendOnChainWrapper()) js.Global().Set("sendOnChain", SendOnChainWrapper())
js.Global().Set("sendOffChain", SendOffChainWrapper()) js.Global().Set("sendOffChain", SendOffChainWrapper())
js.Global().Set("claim", ClaimWrapper())
js.Global().Set("unilateralRedeem", UnilateralRedeemWrapper()) js.Global().Set("unilateralRedeem", UnilateralRedeemWrapper())
js.Global().Set("collaborativeRedeem", CollaborativeRedeemWrapper()) js.Global().Set("collaborativeRedeem", CollaborativeRedeemWrapper())
js.Global().Set("getTransactionHistory", GetTransactionHistoryWrapper())
js.Global().Set("log", LogWrapper()) js.Global().Set("log", LogWrapper())
js.Global().Set("getAspUrl", GetAspUrlWrapper()) js.Global().Set("getAspUrl", GetAspUrlWrapper())

View File

@@ -6,9 +6,12 @@ package browser
import ( import (
"context" "context"
"encoding/hex" "encoding/hex"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"strconv"
"syscall/js" "syscall/js"
"time"
arksdk "github.com/ark-network/ark/pkg/client-sdk" arksdk "github.com/ark-network/ark/pkg/client-sdk"
"github.com/ark-network/ark/pkg/client-sdk/wallet" "github.com/ark-network/ark/pkg/client-sdk/wallet"
@@ -119,21 +122,25 @@ func BalanceWrapper() js.Func {
} }
var ( var (
onchainBalance int onchainSpendableBalance int
offchainBalance int onchainLockedBalance int
offchainBalance int
) )
if resp == nil { if resp != nil {
onchainBalance = 0 onchainSpendableBalance = int(resp.OnchainBalance.SpendableAmount)
offchainBalance = 0 for _, b := range resp.OnchainBalance.LockedAmount {
} else { onchainLockedBalance += int(b.Amount)
onchainBalance = int(resp.OnchainBalance.SpendableAmount) }
offchainBalance = int(resp.OffchainBalance.Total) offchainBalance = int(resp.OffchainBalance.Total)
} }
result := map[string]interface{}{ result := map[string]interface{}{
"onchain_balance": onchainBalance, "onchainBalance": map[string]interface{}{
"offchain_balance": offchainBalance, "spendable": onchainSpendableBalance,
"locked": onchainLockedBalance,
},
"offchainBalance": offchainBalance,
} }
return js.ValueOf(result), nil 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 { func UnilateralRedeemWrapper() js.Func {
return JSPromise(func(args []js.Value) (interface{}, error) { return JSPromise(func(args []js.Value) (interface{}, error) {
return nil, arkSdkClient.UnilateralRedeem(context.Background()) 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 { func GetAspUrlWrapper() js.Func {
return js.FuncOf(func(this js.Value, p []js.Value) interface{} { return js.FuncOf(func(this js.Value, p []js.Value) interface{} {
data, _ := arkSdkClient.GetConfigData(context.Background()) data, _ := arkSdkClient.GetConfigData(context.Background())

View File

@@ -421,7 +421,6 @@ func (s *covenantService) startRound() {
round := domain.NewRound(dustAmount) round := domain.NewRound(dustAmount)
//nolint:all //nolint:all
round.StartRegistration() round.StartRegistration()
s.lastEvent = nil
s.currentRound = round s.currentRound = round
defer func() { defer func() {

View File

@@ -712,7 +712,6 @@ func (s *covenantlessService) startRound() {
round := domain.NewRound(dustAmount) round := domain.NewRound(dustAmount)
//nolint:all //nolint:all
round.StartRegistration() round.StartRegistration()
s.lastEvent = nil
s.currentRound = round s.currentRound = round
defer func() { defer func() {