Usage Guide¶
Install the SDK¶
Using Bun:
Or via CDN (no build process required):
Then import into your dApp:
1. Initialize the SDK¶
Call loop.init() once when your application loads:
loop.init({
appName: 'My Awesome dApp',
network: 'local', // or 'devnet', 'mainnet'
options: {
openMode: 'popup', // or 'tab'
requestSigningMode: 'popup', // 'popup' (default) | 'tab'
redirectUrl: 'https://mydapp.com/after-connect', // optional
},
onAccept: (provider) => {
console.log('Connected!', provider);
},
onReject: () => {
console.log('Connection rejected by user.');
},
});
Parameters¶
| Field | Description |
|---|---|
appName |
Name shown to the user in Loop wallet |
network |
local, devnet, or mainnet |
onAccept(provider) |
Called when the user approves connection |
onReject() |
Called when the user rejects connection |
Options¶
| Field | Description |
|---|---|
openMode |
'popup' (default) or 'tab' |
requestSigningMode |
Controls how signing/transaction requests open the wallet UI: 'popup' (default) or 'tab' |
redirectUrl |
Optional URL your user will return to after connecting |
2. Connect to the Wallet¶
To start the connection:
redirectUrl and openMode are read from the options in init().
This opens a QR modal for the user to scan with their Loop wallet.
If you set requestSigningMode to 'popup' (or 'tab'), the SDK will also open the wallet dashboard for signing/transaction requests and auto-close the popup when the wallet responds.
3. Using the Provider¶
When the user accepts, the provider object gives you access to wallet data and ledger operations.
The provider object includes:
party_idpublic_keyemail
Get Holdings¶
Get Active Contracts¶
By Template ID:
const contracts = await provider.getActiveContracts({
templateId: '#splice-amulet:Splice.Amulet:Amulet'
});
console.log(contracts);
By Interface ID:
const contracts = await provider.getActiveContracts({
interfaceId: '#splice-api-token-holding-v1:Splice.Api.Token.HoldingV1:Holding'
});
console.log(contracts);
Submit a Transaction¶
const damlCommand = {
commands: [{
ExerciseCommand: {
templateId: "#splice-api-token-transfer-instruction-v1:Splice.Api.Token.TransferInstructionV1:TransferFactory",
contractId: 'your-contract-id',
choice: 'TransferFactory_Transfer',
choiceArgument: {
// ... your arguments
}
}
}],
};
try {
const result = await provider.submitTransaction(damlCommand, {
// Optional: show a custom message in the wallet prompt
message: 'Transfer 10 CC to RetailStore',
});
console.log('Transaction successful:', result);
} catch (error) {
console.error('Transaction failed:', error);
}
Sign a Message¶
const message = 'Hello, Loop!';
try {
const signature = await provider.signMessage(message);
console.log('Signature:', signature);
} catch (error) {
console.error('Signing failed:', error);
}
Transfer (built-in helper)¶
// Fast path: uses your wallet connection to build and run a transfer
await loop.wallet.transfer(
'receiver::fingerprint',
'5', // amount as string or number
{
// Optional overrides. Defaults to Amulet/DSO if omitted.
instrument_admin: 'issuer::fingerprint', // optional
instrument_id: 'LOOP', // optional
},
{
// Optional: show a custom message in the wallet prompt
message: 'Send 5 CC to Alice',
requestedAt: new Date().toISOString(), // optional
executeBefore: new Date(Date.now() + 24*60*60*1000).toISOString(), // optional
requestTimeout: 5 * 60 * 1000, // optional (ms), defaults to 5 minutes
},
);
Notes:
- You must have spendable holdings for the specified instrument (admin + id). If left blank, the SDK defaults to the native token.
- The helper handles: fetching holdings, building the transfer factory payload, and submitting via Wallet Connect.
- Requests time out after 5 minutes by default; override with requestTimeout in milliseconds.
USDC withdraw helper¶
await loop.wallet.extension.usdcBridge.withdrawalUSDCxToEthereum(
'0xYourEthAddress',
'10.5',
{
reference: 'optional memo',
message: 'Withdraw 10.5 USDCx to 0xabc', // optional custom prompt text
requestTimeout: 5 * 60 * 1000,
},
);
Notes: - Uses the connect-based withdraw endpoint to prepare the transaction and sends it over Wallet Connect. - The helper auto-reconnects the websocket if it was closed before sending the request.
How the Loop Connect Flow Works¶
This section explains the code path from your dApp to the Loop wallet and back.
1. Your dApp initializes the SDK¶
You call loop.init() once your app loads:
loop.init({
appName: 'My Test dApp',
network: 'devnet',
options: {
openMode: 'popup',
redirectUrl: 'https://mydapp.com/connected',
},
walletUrl,
apiUrl,
onAccept: (provider) => setProvider(provider),
onReject: () => console.log('User rejected connection'),
});
This step only configures the SDK. No connection is made yet.
2. User clicks "Connect" in your dApp¶
When called, the SDK:
- Checks localStorage to see if there is a previous session.
- If not, it asks the Loop backend for a connect ticket.
- Builds the wallet URL:
/.connect/?ticketId=xxxx - Opens the connection flow (QR, popup, or new tab).
- Opens a WebSocket to wait for approve/reject.
If a valid session is already cached, the SDK may skip the QR step and reconnect automatically.
3. User approves in the Loop wallet¶
When the user approves:
- The wallet updates the backend with "approved".
- The backend sends a
handshake_acceptmessage over WebSocket. -
The SDK creates a
Providerobject containing: -
party_id public_keyemail-
authToken -
SDK calls your
onAccept(provider)callback.
At this point, your dApp is connected.
4. Your dApp uses the Provider¶
After you store the provider in your component/state, you can call:
provider.getHolding();
provider.getActiveContracts({ templateId });
provider.submitTransaction(damlCommand);
provider.signMessage("Hello");
This is the same flow used in the CodePen demo. You initialize once, connect on the button click, then use the provider to interact with the wallet and ledger.