This commit is contained in:
Georges KABBOUCHI 2021-08-22 20:35:46 +03:00
parent b5b7141661
commit 63dfc0c4c8
7 changed files with 280 additions and 147 deletions

View File

@ -5,17 +5,19 @@
<div class="h-full overflow-y-scroll scrollbar-hover">
<div class="mx-auto" style="max-width: 296px">
<div class="py-2 sm:py-4">
<pre>{{ $props }}</pre>
<pre>{{ selectedStrategy }}</pre>
<div v-for="(input, index) in inputs" :key="index">
<input
type="text"
:value="input.value"
@input="$event => input.onInput($event.target.value)"
:placeholder="input.placeholder()"
/>
{{ input.error }}
</div>
<input
type="text"
v-for="(input, index) in selectedStrategy.inputs"
:key="index"
:value="input.value"
@input="$event => input.onInput($event.target.value)"
:placeholder="input.placeholder()"
/>
<button @submit="submit">Submit</button>
{{ error }}
</div>
</div>
</div>
@ -23,15 +25,10 @@
</template>
<script lang="ts">
import {
computed,
defineComponent,
watch,
watchEffect,
ref
} from "@nuxtjs/composition-api";
import { defineComponent } from "@nuxtjs/composition-api";
import { useSidebar } from "~/composables/useSidebar";
import { protocolStrategies, DefineStrategy } from "~/core/strategies";
import { useStrategy } from "~/composables/useStrategy";
export default defineComponent({
props: {
@ -50,18 +47,14 @@ export default defineComponent({
const strategies: DefineStrategy[] =
protocolStrategies[props.protocol] || [];
const selectedStrategy = ref(
const { inputs, submit, error } = useStrategy(
strategies.find(strategy => strategy.id === props.strategy)
);
watch([], () => {
selectedStrategy.value = strategies.find(
strategy => strategy.id === props.strategy
);
});
return {
selectedStrategy
inputs,
error,
submit
};
}
});

View File

@ -20,8 +20,14 @@ import { useBigNumber } from "./useBigNumber";
import { useSorting } from "./useSorting";
const balances = reactive({
user: null,
dsa: null
user: {
mainnet: {},
polygon: {}
},
dsa: {
mainnet: {},
polygon: {}
}
});
const prices = reactive({

View File

@ -0,0 +1,60 @@
import { nextTick, onMounted, ref, watch } from "@nuxtjs/composition-api";
import { buildStrategy, DefineStrategy, IStrategy } from "~/core/strategies";
import { useBalances } from "./useBalances";
import { useDSA } from "./useDSA";
import { useWeb3 } from "./useWeb3";
export function useStrategy(defineStrategy: DefineStrategy) {
const { web3, networkName } = useWeb3();
const { dsa } = useDSA();
const { prices, balances } = useBalances();
const strategy = buildStrategy(defineStrategy);
const inputs = ref(strategy.getInputs());
const error = ref("");
strategy.onUpdated(async () => {
await nextTick();
inputs.value = strategy.getInputs();
});
const submit = async () => {
try {
await strategy.submit();
} catch (e) {
error.value = e.message;
}
};
watch(web3, () => strategy.setWeb3(web3.value), { immediate: true });
watch(dsa, () => strategy.setDSA(dsa.value), { immediate: true });
watch(
prices,
() => strategy.setProps({ prices: prices[networkName.value] }),
{ immediate: true }
);
watch(
balances,
() => {
strategy.setProps({
dsaTokens: balances.dsa[networkName.value],
userTokens: balances.user[networkName.value]
});
},
{ immediate: true }
);
// testing
onMounted(() => {
//@ts-ignore
window.strategy = strategy;
});
return {
strategy,
inputs,
submit,
error,
};
}

View File

@ -0,0 +1,84 @@
import DSA, { Spell } from "dsa-connect";
import Web3 from "web3";
import slugify from "slugify";
import { Strategy } from "./strategy";
export interface IStrategyContext {
dsa: DSA;
web3: Web3;
inputs: IStrategyInput<StrategyInputType>[];
dsaTokens?: IStrategyToken,
userTokens?: IStrategyToken,
}
export interface IStrategyToken {
address: string;
key: string;
symbol: string;
balance: string;
// supply: string;
// borrow: string;
}
export enum StrategyInputType {
INPUT = "input",
INPUT_WITH_TOKEN = "input-with-token"
}
export type StrategyInputParameterMap = {
[StrategyInputType.INPUT]: {};
[StrategyInputType.INPUT_WITH_TOKEN]: {
token?: IStrategyToken;
};
};
export interface IStrategyInput<InputType extends StrategyInputType> {
type: InputType;
name: string;
placeholder?: (
context: IStrategyContext & {
input: IStrategyInput<InputType> & StrategyInputParameterMap[InputType];
}
) => string;
validate?: (
context: IStrategyContext & {
input: IStrategyInput<InputType> & StrategyInputParameterMap[InputType];
}
) => string | void;
value?: any;
[key: string]: any;
}
export interface IStrategy {
id?: string;
name: string;
description: string;
author?: string;
inputs: IStrategyInput<StrategyInputType>[];
spells: (context: IStrategyContext) => Promise<Spell[]> | Spell[];
submitText?: string;
}
export function defineInput<InputType extends StrategyInputType>(
input: IStrategyInput<InputType>
) {
return input as IStrategyInput<InputType>;
}
export function defineStrategy(strategy: IStrategy) {
return {
...strategy,
id: strategy.id ? strategy.id : slugify(strategy.name).toLowerCase()
};
}
export function buildStrategy(schema: DefineStrategy) {
return new Strategy(schema)
}
export type DefineStrategy = ReturnType<typeof defineStrategy>;

View File

@ -1,140 +1,130 @@
import DSA, { Spell } from "dsa-connect";
import DSA from "dsa-connect";
import Web3 from "web3";
import slugify from "slugify";
export interface IStrategyContext {
dsa: DSA;
web3: Web3;
inputs: IStrategyInput<StrategyInputType>[];
}
import { DefineStrategy, IStrategyContext } from ".";
export interface IStrategyToken {
address: string;
key: string;
symbol: string;
balance: string;
supply: string;
borrow: string;
}
export enum StrategyInputType {
INPUT = "input",
INPUT_WITH_TOKEN = "input-with-token"
}
export type StrategyInputParameterMap = {
[StrategyInputType.INPUT]: {};
[StrategyInputType.INPUT_WITH_TOKEN]: {
token?: IStrategyToken;
};
};
export interface IStrategyInput<InputType extends StrategyInputType> {
type: InputType;
name: string;
placeholder: (
context: IStrategyContext & {
input: IStrategyInput<InputType> & StrategyInputParameterMap[InputType];
}
) => string;
validate?: (
context: IStrategyContext & {
input: IStrategyInput<InputType> & StrategyInputParameterMap[InputType];
}
) => string | void;
value?: any;
[key: string]: any;
}
export interface IStrategy {
id?: string;
name: string;
description: string;
author?: string;
inputs: IStrategyInput<any>[];
spells: (context: IStrategyContext) => Promise<Spell[]> | Spell[];
submitText?: string;
}
export function defineInput<InputType extends StrategyInputType>(
input: IStrategyInput<InputType>
) {
return input as IStrategyInput<any>;
}
export function defineStrategy(strategy: IStrategy) {
const context = {
export class Strategy {
schema: DefineStrategy;
inputs = [];
context = {
web3: null,
dsa: null
};
return {
...strategy,
id: strategy.id ? strategy.id : slugify(strategy.name).toLowerCase(),
inputs: strategy.inputs.map(input => ({
listeners = [];
props: object = {
prices: {},
dsaTokens: {},
userTokens: {},
};
constructor(schema: DefineStrategy) {
this.schema = schema;
this.inputs = this.schema.inputs;
}
getContext(): IStrategyContext {
return {
...this.context,
...this.props,
inputs: this.getInputs()
};
}
setProps(props: object) {
Object.assign(this.props, props);
}
getInputs() {
return this.inputs.map(input => ({
...input,
value: null,
value: input.value || "",
error: input.error || "",
placeholder: () =>
input.placeholder
? input.placeholder({
...context,
inputs: strategy.inputs,
...this.context,
inputs: this.inputs,
input: {
...input,
token: {
// todo
// todo
}
}
})
: null,
onInput: (val: any) => {
input.error = "";
input.value = val;
}
})),
submit: async () => {
await this.validate({
...context,
inputs: strategy.inputs
});
const allSpells = await strategy.spells({
...context,
inputs: strategy.inputs
});
const spells = context.dsa.Spell();
for (const spell of allSpells) {
spells.add(spell);
}
return await context.dsa.cast({
spells,
onReceipt: this.onReceipt
});
},
validate: async () => {
for (const input of this.inputs) {
const result = await input.validate({
...context,
inputs: strategy.inputs,
input
});
if (result !== true) {
throw new Error(result || "Error has occurred");
if (val) {
input.error = input.validate({
...this.getContext(),
input
});
}
}
},
onReceipt: (txHash: string, txReceipt: any) => {
// do something
}
};
}
export type DefineStrategy = ReturnType<typeof defineStrategy>;
this.notifyListeners();
}
}));
}
async submit() {
await this.validate();
const allSpells = await this.schema.spells(this.getContext());
const spells = this.context.dsa.Spell();
console.log(spells);
for (const spell of allSpells) {
spells.add(spell);
}
return await this.context.dsa.cast({
spells,
onReceipt: this.onReceipt
});
}
async validate() {
const inputs = this.getInputs();
for (const input of inputs) {
const result = await input.validate({
...this.getContext(),
input
});
if (typeof result === "string") {
throw new Error(result || "Error has occurred");
}
}
}
onReceipt(txHash: string, txReceipt: any) {
// do something
}
setWeb3(web3: Web3) {
console.log(web3);
this.context.web3 = web3;
}
setDSA(dsa: DSA) {
this.context.dsa = dsa;
}
async notifyListeners() {
for (const listener of this.listeners) {
await listener(this);
}
}
onUpdated(cb) {
this.listeners.push(cb);
}
}

View File

@ -4,4 +4,4 @@ export const protocolStrategies = {
aaveV2 : AaveV2,
}
export * from "./helpers/strategy"
export * from "./helpers"

View File

@ -2,7 +2,7 @@ import {
defineStrategy,
defineInput,
StrategyInputType
} from "../../helpers/strategy";
} from "../../helpers";
export default defineStrategy({
name: "Deposit & Borrow",