diff --git a/src/abi-types.ts b/src/abi-types.ts
index 7770a8fa..309d40c5 100644
--- a/src/abi-types.ts
+++ b/src/abi-types.ts
@@ -121,6 +121,24 @@ export class REXDeposit extends Struct {
@Struct.field('asset') amount!: Asset
}
+@Struct.type('rexwithdraw')
+export class REXWithdraw extends Struct {
+ @Struct.field('name') owner!: Name
+ @Struct.field('asset') amount!: Asset
+}
+
+@Struct.type('rexbuyrex')
+export class REXBUYREX extends Struct {
+ @Struct.field('name') from!: Name
+ @Struct.field('asset') amount!: Asset
+}
+
+@Struct.type('rexsellrex')
+export class REXSELLREX extends Struct {
+ @Struct.field('name') from!: Name
+ @Struct.field('asset') rex!: Asset
+}
+
@Struct.type('rexrentcpu')
export class REXRentCPU extends Struct {
@Struct.field('name') from!: Name
diff --git a/src/app.svelte b/src/app.svelte
index b6c8a310..3533c14d 100644
--- a/src/app.svelte
+++ b/src/app.svelte
@@ -8,6 +8,7 @@
import Login from '~/pages/login.svelte'
import Dashboard from '~/pages/dashboard/index.svelte'
+ import Earn from '~/pages/earn/index.svelte'
import Request from '~/pages/request/index.svelte'
import Send from '~/pages/send/index.svelte'
import TokensPurchase from '~/pages/tokens/purchase/index.svelte'
@@ -178,6 +179,9 @@
+
+
+
diff --git a/src/components/elements/input/token/selector.svelte b/src/components/elements/input/token/selector.svelte
index ddf1bedf..e66d60cc 100644
--- a/src/components/elements/input/token/selector.svelte
+++ b/src/components/elements/input/token/selector.svelte
@@ -1,6 +1,5 @@
+
+
+
+
+
+
+
+
+
(thumbHover = true)}
+ on:focus={() => (thumbHover = true)}
+ on:mouseout={() => (thumbHover = false)}
+ on:blur={() => (thumbHover = false)}
+ >
+ {#if holding || thumbHover}
+
+ {value + ' %'}
+
+ {/if}
+
+
+
+
+
+
+
+
diff --git a/src/components/layout/navigation/index.svelte b/src/components/layout/navigation/index.svelte
index 4f77dd0c..b728a047 100644
--- a/src/components/layout/navigation/index.svelte
+++ b/src/components/layout/navigation/index.svelte
@@ -6,6 +6,7 @@
import {activeBlockchain, preferences} from '~/store'
import type {NavigationItem} from '~/ui-types'
import {banxaIsAvailable} from '~/lib/banxa'
+ import {rexIsAvailable} from '~/lib/rex'
import MediaQuery from '~/components/utils/media-query.svelte'
import NavigationContent from '~/components/layout/navigation/content.svelte'
@@ -30,6 +31,16 @@
name: 'Transfer',
path: '/transfer',
},
+ ...(rexIsAvailable($activeBlockchain)
+ ? [
+ {
+ icon: 'dollar-sign',
+ name: 'Earn',
+ path: '/earn',
+ },
+ ]
+ : []),
+
...(banxaIsAvailable($activeBlockchain)
? [
{
diff --git a/src/config.ts b/src/config.ts
index 4df19377..98f8a0fb 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -78,6 +78,8 @@ export interface ChainConfig {
banxaEnabled?: boolean
/** Banxa coin_code */
banxa_coin_code?: string
+ /** Is REX available for this chain */
+ rexEnabled?: boolean
}
/** Supported chains. */
@@ -105,6 +107,7 @@ export const chains: ChainConfig[] = [
bloksUrl: 'https://bloks.io',
balanceProviders: new Set([BalanceProviders.LightAPI]),
banxaEnabled: true,
+ rexEnabled: true,
},
{
id: 'fio',
@@ -172,6 +175,7 @@ export const chains: ChainConfig[] = [
nodeUrl: 'https://jungle4.greymass.com',
testnet: true,
bloksUrl: 'https://eosauthority.com/?network=jungle',
+ rexEnabled: true,
},
{
id: 'proton',
diff --git a/src/lib/rex.ts b/src/lib/rex.ts
new file mode 100644
index 00000000..8bc62568
--- /dev/null
+++ b/src/lib/rex.ts
@@ -0,0 +1,5 @@
+import type {ChainConfig} from '~/config'
+
+export const rexIsAvailable = (chainData: ChainConfig | undefined): boolean => {
+ return !!chainData?.rexEnabled
+}
diff --git a/src/pages/earn/components/label.svelte b/src/pages/earn/components/label.svelte
new file mode 100644
index 00000000..31e1e4de
--- /dev/null
+++ b/src/pages/earn/components/label.svelte
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
{header}
+ {#if subheader}
+
{subheader}
+ {/if}
+
+
+
+
+
+ {#if changeStep}
+
+
+
+ {/if}
+
diff --git a/src/pages/earn/components/progress.svelte b/src/pages/earn/components/progress.svelte
new file mode 100644
index 00000000..c277f404
--- /dev/null
+++ b/src/pages/earn/components/progress.svelte
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/src/pages/earn/index.svelte b/src/pages/earn/index.svelte
new file mode 100644
index 00000000..05f286bc
--- /dev/null
+++ b/src/pages/earn/index.svelte
@@ -0,0 +1,353 @@
+
+
+
+
+
+
+ {#if $step === Step.Error}
+ switchStep($defaultStep)} />
+ {:else if $step === Step.Bootstrap}
+ toStakeConfirm(Step.Bootstrap)}
+ />
+ {:else if $step === Step.Overview}
+ switchStep(Step.Stake)}
+ toUnstake={() => switchStep(Step.Unstake)}
+ toClaim={() => switchStep(Step.Claim)}
+ />
+ {:else if $step === Step.Stake}
+ toStakeConfirm(Step.Stake)}
+ handleBack={() => switchStep($defaultStep)}
+ />
+ {:else if $step === Step.Unstake}
+ toStakeConfirm(Step.Unstake)}
+ handleBack={() => switchStep($defaultStep)}
+ />
+ {:else if $step === Step.Claim}
+ toStakeConfirm(Step.Claim)}
+ handleBack={() => switchStep($defaultStep)}
+ />
+ {:else if $step === Step.Confirm}
+
+ {/if}
+
+
diff --git a/src/pages/earn/step/bootstrap.svelte b/src/pages/earn/step/bootstrap.svelte
new file mode 100644
index 00000000..68d3d4fb
--- /dev/null
+++ b/src/pages/earn/step/bootstrap.svelte
@@ -0,0 +1,150 @@
+
+
+
+
+
+
+
+
+
+
+
+
The unstaking process takes 21 days before the tokens become available for claim.
+
+
* The APY changes based on the total number of EOS tokens staked at any given moment.
+
+
diff --git a/src/pages/earn/step/claim.svelte b/src/pages/earn/step/claim.svelte
new file mode 100644
index 00000000..dbd090ec
--- /dev/null
+++ b/src/pages/earn/step/claim.svelte
@@ -0,0 +1,190 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/earn/step/confirm.svelte b/src/pages/earn/step/confirm.svelte
new file mode 100644
index 00000000..d5e71241
--- /dev/null
+++ b/src/pages/earn/step/confirm.svelte
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+ {action}
+
+
+
+
+
+
+ {amount}
+
+ {#if usd}
+ ≈ $ {usd} USD
+ {/if}
+
+
+
+
+
+
diff --git a/src/pages/earn/step/error.svelte b/src/pages/earn/step/error.svelte
new file mode 100644
index 00000000..7db1791c
--- /dev/null
+++ b/src/pages/earn/step/error.svelte
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
Transfer Failed
+
+
+
+
+
+
+
+
diff --git a/src/pages/earn/step/overview.svelte b/src/pages/earn/step/overview.svelte
new file mode 100644
index 00000000..15c8c07d
--- /dev/null
+++ b/src/pages/earn/step/overview.svelte
@@ -0,0 +1,198 @@
+
+
+
+
+
+
+
+
+
+
+
+
currently staked balance
+
+ {rexInfo.total}
+
+ {#if Number(rexInfo.savings.value) > 0}
+
+ {rexInfo.savings} unstakable
+
+ {/if}
+ {#if Number(rexInfo.matured.value) > 0}
+
+ {rexInfo.matured} claimable
+
+ {/if}
+
+
+
+
+
+
+
+
+
+
The unstaking process takes 21 days before the tokens become available for claim.
+
+
* The APY changes based on the total number of EOS tokens staked at any given moment.
+
+
diff --git a/src/pages/earn/step/stake.svelte b/src/pages/earn/step/stake.svelte
new file mode 100644
index 00000000..112bf3fa
--- /dev/null
+++ b/src/pages/earn/step/stake.svelte
@@ -0,0 +1,178 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/earn/step/unstake.svelte b/src/pages/earn/step/unstake.svelte
new file mode 100644
index 00000000..63f8e1ae
--- /dev/null
+++ b/src/pages/earn/step/unstake.svelte
@@ -0,0 +1,190 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/earn/types.ts b/src/pages/earn/types.ts
new file mode 100644
index 00000000..55a33213
--- /dev/null
+++ b/src/pages/earn/types.ts
@@ -0,0 +1,19 @@
+import type {Asset} from 'anchor-link'
+
+export const enum Step {
+ Bootstrap,
+ Overview,
+ Stake,
+ Unstake,
+ Claim,
+ Confirm,
+ Success,
+ Error,
+}
+
+export interface REXInfo {
+ total: Asset
+ savings: Asset
+ matured: Asset
+ apy: string
+}
diff --git a/src/stores/account-provider.ts b/src/stores/account-provider.ts
index 2562596e..e5505569 100644
--- a/src/stores/account-provider.ts
+++ b/src/stores/account-provider.ts
@@ -109,6 +109,16 @@ export async function loadAccount(
if (row) {
const age = Date.now() - row.updated.getTime()
stale = age > maxAge
+ // TODO: Remove this once Wharf is implemented
+ // This was needed to fix a bug where the last_vote_weight and proxied_vote_weight fields were missing
+ if (row.account && row.account.voter_info) {
+ if (!row.account.voter_info.last_vote_weight) {
+ row.account.voter_info.last_vote_weight = '0'
+ }
+ if (!row.account.voter_info.proxied_vote_weight) {
+ row.account.voter_info.proxied_vote_weight = '0'
+ }
+ }
set({account: API.v1.AccountObject.from(row.account), stale})
}
if (stale || refresh) {