Skip to content

Commit

Permalink
Merge pull request #2975 from dusk-network/feature-2974
Browse files Browse the repository at this point in the history
web-wallet: Implement `w3sper`'s staking methods in the wallet store
  • Loading branch information
ascartabelli authored Nov 14, 2024
2 parents ae6d35c + d0da061 commit 7eb711e
Show file tree
Hide file tree
Showing 13 changed files with 477 additions and 50 deletions.
4 changes: 4 additions & 0 deletions web-wallet/src/__mocks__/ProtocolDriver.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export async function generateProfile(seed, n) {
return new Uint8Array(64 + 96).fill(99);
}

export async function getMinimumStake() {
return 1_000_000_000_000n;
}

export function load() {}

export async function unload() {}
1 change: 1 addition & 0 deletions web-wallet/src/__mocks__/mockedWalletStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const content = {
balance: { shielded, unshielded },
currentProfile,
initialized: true,
minimumStake: 1_000_000_000_000n,
profiles,
stakeInfo,
syncStatus: {
Expand Down
2 changes: 1 addition & 1 deletion web-wallet/src/lib/components/Stake/Stake.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@
errorMessage="Transaction failed"
onBeforeLeave={resetOperation}
operation={flow === "stake"
? execute(stakeAmount, gasPrice, gasLimit)
? execute(duskToLux(stakeAmount), gasPrice, gasLimit)
: execute(gasPrice, gasLimit)}
pendingMessage="Processing transaction"
successMessage="Transaction completed"
Expand Down
12 changes: 8 additions & 4 deletions web-wallet/src/lib/components/__tests__/Stake.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import {
} from "vitest";
import { cleanup, fireEvent, render } from "@testing-library/svelte";
import { deductLuxFeeFrom } from "$lib/contracts";
import { createCurrencyFormatter, luxToDusk } from "$lib/dusk/currency";
import {
createCurrencyFormatter,
duskToLux,
luxToDusk,
} from "$lib/dusk/currency";

import { Stake } from "..";

Expand Down Expand Up @@ -237,7 +241,7 @@ describe("Stake", () => {

expect(baseProps.execute).toHaveBeenCalledTimes(1);
expect(baseProps.execute).toHaveBeenCalledWith(
baseProps.minAllowedStake,
duskToLux(baseProps.minAllowedStake),
baseProps.gasSettings.gasPrice,
baseProps.gasSettings.gasLimit
);
Expand Down Expand Up @@ -265,7 +269,7 @@ describe("Stake", () => {

expect(baseProps.execute).toHaveBeenCalledTimes(1);
expect(baseProps.execute).toHaveBeenCalledWith(
2567,
duskToLux(2567),
baseProps.gasSettings.gasPrice,
baseProps.gasSettings.gasLimit
);
Expand All @@ -286,7 +290,7 @@ describe("Stake", () => {

expect(baseProps.execute).toHaveBeenCalledTimes(1);
expect(baseProps.execute).toHaveBeenCalledWith(
maxSpendable,
duskToLux(maxSpendable),
baseProps.gasSettings.gasPrice,
baseProps.gasSettings.gasLimit
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
/** @type {Record<StakeType, (...args: any[]) => Promise<string>>} */
const executeOperations = {
stake: (amount, gasLimit, gasPrice) =>
stake: (amount, gasPrice, gasLimit) =>
walletStore
.stake(amount, new Gas({ limit: gasLimit, price: gasPrice }))
.then(getLastTransactionHash),
Expand All @@ -74,7 +74,10 @@
.then(getLastTransactionHash),
"withdraw-rewards": (gasPrice, gasLimit) =>
walletStore
.withdrawReward(new Gas({ limit: gasLimit, price: gasPrice }))
.withdrawReward(
$walletStore.stakeInfo.reward,
new Gas({ limit: gasLimit, price: gasPrice })
)
.then(getLastTransactionHash),
};
Expand Down
9 changes: 9 additions & 0 deletions web-wallet/src/lib/stores/__tests__/walletStore.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ describe("Wallet store", async () => {
nonce: 1234n,
value: shielded.value / 2n,
};
const minimumStake = 1_000_000_000_000n;

vi.spyOn(Bookkeeper.prototype, "minimumStake", "get").mockResolvedValue(
minimumStake
);

const setTimeoutSpy = vi.spyOn(window, "setTimeout");
const clearTimeoutSpy = vi.spyOn(window, "clearTimeout");
Expand All @@ -72,6 +77,7 @@ describe("Wallet store", async () => {
? shielded
: unshielded;
});

const stakeInfoSpy = vi
.spyOn(Bookkeeper.prototype, "stakeInfo")
.mockImplementation(async () => stakeInfo);
Expand Down Expand Up @@ -109,6 +115,7 @@ describe("Wallet store", async () => {
},
currentProfile: null,
initialized: false,
minimumStake: 0n,
profiles: [],
stakeInfo: {
amount: null,
Expand All @@ -131,6 +138,7 @@ describe("Wallet store", async () => {
balance: { shielded, unshielded },
currentProfile: defaultProfile,
initialized: true,
minimumStake,
profiles: [defaultProfile],
stakeInfo,
};
Expand All @@ -155,6 +163,7 @@ describe("Wallet store", async () => {
balance: cachedBalance,
currentProfile: defaultProfile,
initialized: true,
minimumStake,
profiles: [defaultProfile],
stakeInfo: cachedStakeInfo,
syncStatus: {
Expand Down
4 changes: 3 additions & 1 deletion web-wallet/src/lib/stores/stores.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ type WalletStoreContent = {
balance: WalletStoreBalance;
currentProfile: import("$lib/vendor/w3sper.js/src/mod").Profile | null;
initialized: boolean;
minimumStake: bigint;
profiles: Array<import("$lib/vendor/w3sper.js/src/mod").Profile>;
stakeInfo: StakeInfo;
syncStatus: {
Expand Down Expand Up @@ -123,7 +124,7 @@ type WalletStoreServices = {
) => Promise<TransactionInfo>;

stake: (
amount: number,
amount: bigint,
gas: import("$lib/vendor/w3sper.js/src/mod").Gas
) => Promise<any>;

Expand All @@ -143,6 +144,7 @@ type WalletStoreServices = {
unstake: (gas: import("$lib/vendor/w3sper.js/src/mod").Gas) => Promise<any>;

withdrawReward: (
amount: bigint,
gas: import("$lib/vendor/w3sper.js/src/mod").Gas
) => Promise<any>;
};
Expand Down
60 changes: 41 additions & 19 deletions web-wallet/src/lib/stores/walletStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const initialState = {
},
currentProfile: null,
initialized: false,
minimumStake: 0n,
profiles: [],
stakeInfo: {
amount: null,
Expand All @@ -63,9 +64,6 @@ const bookkeeper = new Bookkeeper(treasury);

const getCurrentProfile = () => get(walletStore).currentProfile;

/** @type {(...args: any) => Promise<void>} */
const asyncNoopFailure = () => Promise.reject(new Error("Not implemented"));

/** @type {(txInfo: TransactionInfo) => void} */
const observeTxRemoval = (txInfo) => {
networkStore.connect().then((network) =>
Expand Down Expand Up @@ -198,6 +196,7 @@ async function init(profileGenerator, syncFromBlock) {
const cachedStakeInfo = await walletCache.getStakeInfo(
currentProfile.account.toString()
);
const minimumStake = await bookkeeper.minimumStake;

treasury.setProfiles([currentProfile]);

Expand All @@ -206,6 +205,7 @@ async function init(profileGenerator, syncFromBlock) {
balance: cachedBalance,
currentProfile,
initialized: true,
minimumStake,
profiles: [currentProfile],
stakeInfo: cachedStakeInfo,
});
Expand Down Expand Up @@ -238,9 +238,27 @@ async function setCurrentProfile(profile) {
);
}

/** @type {WalletStoreServices["shield"]} */
const shield = async (amount, gas) =>
sync()
.then(networkStore.connect)
.then((network) =>
network.execute(
bookkeeper.as(getCurrentProfile()).shield(amount).gas(gas)
)
)
.then(updateCacheAfterTransaction)
.then(passThruWithEffects(observeTxRemoval));

/** @type {WalletStoreServices["stake"]} */
const stake = async (amount, gasSettings) =>
asyncNoopFailure(amount, gasSettings);
const stake = async (amount, gas) =>
sync()
.then(networkStore.connect)
.then((network) =>
network.execute(bookkeeper.as(getCurrentProfile()).stake(amount).gas(gas))
)
.then(updateCacheAfterTransaction)
.then(passThruWithEffects(observeTxRemoval));

/** @type {WalletStoreServices["sync"]} */
async function sync(fromBlock) {
Expand Down Expand Up @@ -344,18 +362,6 @@ async function sync(fromBlock) {
return syncPromise;
}

/** @type {WalletStoreServices["shield"]} */
const shield = async (amount, gas) =>
sync()
.then(networkStore.connect)
.then((network) =>
network.execute(
bookkeeper.as(getCurrentProfile()).shield(amount).gas(gas)
)
)
.then(updateCacheAfterTransaction)
.then(passThruWithEffects(observeTxRemoval));

/** @type {WalletStoreServices["transfer"]} */
const transfer = async (to, amount, gas) =>
sync()
Expand Down Expand Up @@ -388,10 +394,26 @@ const unshield = async (amount, gas) =>
.then(passThruWithEffects(observeTxRemoval));

/** @type {WalletStoreServices["unstake"]} */
const unstake = async (gasSettings) => asyncNoopFailure(gasSettings);
const unstake = async (gas) =>
sync()
.then(networkStore.connect)
.then((network) =>
network.execute(bookkeeper.as(getCurrentProfile()).unstake().gas(gas))
)
.then(updateCacheAfterTransaction)
.then(passThruWithEffects(observeTxRemoval));

/** @type {WalletStoreServices["withdrawReward"]} */
const withdrawReward = async (gasSettings) => asyncNoopFailure(gasSettings);
const withdrawReward = async (amount, gas) =>
sync()
.then(networkStore.connect)
.then((network) =>
network.execute(
bookkeeper.as(getCurrentProfile()).withdraw(amount).gas(gas)
)
)
.then(updateCacheAfterTransaction)
.then(passThruWithEffects(observeTxRemoval));

/** @type {WalletStore} */
export default {
Expand Down
35 changes: 29 additions & 6 deletions web-wallet/src/lib/vendor/w3sper.js/src/bookkeeper.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import {
Transfer,
UnshieldTransfer,
ShieldTransfer,
StakeTransfer,
UnstakeTransfer,
WithdrawStakeRewardTransfer,
} from "../src/transaction.js";

class BookEntry {
Expand All @@ -22,12 +25,16 @@ class BookEntry {
Object.freeze(this);
}

balance(type) {
return this.bookkeeper.balance(this.profile[type]);
}

stakeInfo() {
return this.bookkeeper.stakeInfo(this.profile.account);
get info() {
const entry = this;
return {
balance(type) {
return entry.bookkeeper.balance(entry.profile[type]);
},
stake() {
return entry.bookkeeper.stakeInfo(entry.profile.account);
},
};
}

transfer(amount) {
Expand All @@ -41,6 +48,18 @@ class BookEntry {
shield(amount) {
return new ShieldTransfer(this).amount(amount);
}

stake(amount) {
return new StakeTransfer(this).amount(amount);
}

unstake() {
return new UnstakeTransfer(this);
}

withdraw(amount) {
return new WithdrawStakeRewardTransfer(this).amount(amount);
}
}

export class Bookkeeper {
Expand All @@ -64,6 +83,10 @@ export class Bookkeeper {
}
}

get minimumStake() {
return ProtocolDriver.getMinimumStake();
}

stakeInfo(identifier) {
const type = ProfileGenerator.typeOf(String(identifier));
if (type !== "account") {
Expand Down
10 changes: 6 additions & 4 deletions web-wallet/src/lib/vendor/w3sper.js/src/network/mod.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export class Network {
#rues;
node;
contracts;
blocks;
transactions;

static LOCALNET = Node.CHAIN.LOCALNET;
static MAINNET = Node.CHAIN.MAINNET;
Expand All @@ -55,10 +57,6 @@ export class Network {
return this.#rues;
}

static connect(url, options = {}) {
return new Network(url).connect(options);
}

async connect(options = {}) {
await this.#rues.connect(options);

Expand Down Expand Up @@ -128,4 +126,8 @@ export class Network {
);
}
}

static connect(url, options = {}) {
return new Network(url).connect(options);
}
}
Loading

0 comments on commit 7eb711e

Please sign in to comment.