Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deposit #1130

Closed
wants to merge 8 commits into from
Closed

Deposit #1130

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions projects/sdk/src/lib/silo/Convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class Convert {
Convert.sdk = sdk;

this.paths = new Map<Token, ERC20Token[]>();

// BEAN<>LP
this.paths.set(Convert.sdk.tokens.BEAN, [
Convert.sdk.tokens.BEAN,
Expand Down Expand Up @@ -178,7 +178,7 @@ export class Convert {
// Ensure token instances.
const fromToken = Convert.sdk.tokens.findByAddress(_fromToken.address);
const toToken = Convert.sdk.tokens.findByAddress(_toToken.address);

if (!fromToken || !toToken) {
throw new Error(`Unknown token ${_fromToken.address} or ${_toToken.address}`);
}
Expand All @@ -188,7 +188,9 @@ export class Convert {
const isToWlLP = Convert.sdk.tokens.wellLP.has(toToken);

if (deprecatedLPs.has(fromToken) || deprecatedLPs.has(toToken)) {
throw new Error(`Deprecated LP conversion pathway: ${fromToken.address} -> ${toToken.address}`);
throw new Error(
`Deprecated LP conversion pathway: ${fromToken.address} -> ${toToken.address}`
);
}

if (fromToken.equals(toToken)) {
Expand Down
75 changes: 47 additions & 28 deletions projects/sdk/src/lib/swapV2/BeanSwap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export interface BeanSwapNodeQuote {
sellToken: ERC20Token | NativeToken;
buyToken: ERC20Token | NativeToken;
sellAmount: TokenValue;
buyAmount: TokenValue;
buyAmount: TokenValue;
minBuyAmount: TokenValue;
nodes: ReadonlyArray<SwapNode>;
slippage: number;
Expand Down Expand Up @@ -105,7 +105,7 @@ export class BeanSwapOperation {
}

getFarm() {
return this.#builder.advancedFarm;
return this.#builder.workflow;
}

get quote() {
Expand All @@ -122,16 +122,21 @@ export class BeanSwapOperation {

/**
* Estimates the swap based on the amount and slippage
* @param amount
* @param slippage
* @param force
* @returns
* @param amount
* @param slippage
* @param force
* @returns
*/
async estimateSwap(amount: TokenValue, slippage: number, force?: boolean) {
if (amount.lte(0)) return;

if (this.#shouldFetchQuote(amount, slippage) || force === true) {
this.#quoteData = await this.quoter.route(this.inputToken, this.targetToken, amount, slippage);
this.#quoteData = await this.quoter.route(
this.inputToken,
this.targetToken,
amount,
slippage
);
this.#buildQuoteData();
await this.estimate();
}
Expand All @@ -145,33 +150,35 @@ export class BeanSwapOperation {
async estimate() {
if (!this.#quoteData) {
throw new Error("Cannot estimate without quote data.");
};
return this.#builder.advancedFarm.estimate(this.#quoteData.sellAmount.toBigNumber());
}
return this.#builder.workflow.estimate(this.#quoteData.sellAmount.toBigNumber());
}

async estimateGas(): Promise<TokenValue> {
// run estimate if not already done
if (!this.#builder.advancedFarm.length) {
if (!this.#builder.workflow.length) {
await this.estimate();
}
if (!this.#builder.advancedFarm.length || !this.#quoteData) {
if (!this.#builder.workflow.length || !this.#quoteData) {
throw new Error("Invalid swap configuration. Cannot estimate gas.");
}
const gas = await this.#builder.advancedFarm.estimateGas(
this.#quoteData.sellAmount.toBigNumber(), {
slippage: this.#quoteData.slippage
});
const gas = await this.#builder.workflow.estimateGas(
this.#quoteData.sellAmount.toBigNumber(),
{
slippage: this.#quoteData.slippage
}
);
return TokenValue.fromBlockchain(gas, 0);
}

async execute(overrides: CallOverrides = {}) {
if (!this.#builder.advancedFarm.length) {
if (!this.#builder.workflow.length) {
await this.estimate();
}
if (!this.#builder.advancedFarm.length || !this.#quoteData) {
if (!this.#builder.workflow.length || !this.#quoteData) {
throw new Error("Invalid swap configuration. Run estimate first.");
}
return this.#builder.advancedFarm.execute(
return this.#builder.workflow.execute(
this.#quoteData.sellAmount,
{ slippage: this.#quoteData.slippage },
overrides
Expand All @@ -180,7 +187,13 @@ export class BeanSwapOperation {

#buildQuoteData() {
if (!this.#quoteData) return;
this.#builder.translateNodesToWorkflow(this.#quoteData.nodes, this.fromMode, this.toMode, this.caller, this.recipient);
this.#builder.translateNodesToWorkflow(
this.#quoteData.nodes,
this.fromMode,
this.toMode,
this.caller,
this.recipient
);
}

#shouldFetchQuote(amount: TokenValue, slippage: number) {
Expand All @@ -193,23 +206,29 @@ export class BeanSwapOperation {

/**
* Build a swap operation w/ quote data via Beanswap.quoter.
* @param quoteData
* @param caller
* @param recipient
* @param fromMode
* @param toMode
* @returns
* @param quoteData
* @param caller
* @param recipient
* @param fromMode
* @param toMode
* @returns
*/
static buildWithQuote(quoteData: BeanSwapNodeQuote, caller: string, recipient: string, fromMode: FarmFromMode, toMode: FarmToMode) {
static buildWithQuote(
quoteData: BeanSwapNodeQuote,
caller: string,
recipient: string,
fromMode: FarmFromMode,
toMode: FarmToMode
) {
const swap = new BeanSwapOperation(
BeanSwap.sdk,
BeanSwap.sdk.beanSwap.quoter,
quoteData.sellToken,
quoteData.buyToken,
caller,
caller,
recipient,
fromMode,
toMode,
toMode
);
swap.#quoteData = quoteData;
swap.#buildQuoteData();
Expand Down
60 changes: 43 additions & 17 deletions projects/sdk/src/lib/swapV2/BeanSwapBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import {
UnwrapEthSwapNode,
WellSwapNode,
WrapEthSwapNode,
ZeroXSwapNode
} from "./nodes";
import { TokenValue } from "@beanstalk/sdk-core";
import { TransferTokenNode } from "./nodes/TransferTokenNode";
import { WellSyncSwapNode } from "./nodes/ERC20SwapNode";

class Builder {
private static sdk: BeanstalkSDK;
Expand All @@ -28,7 +30,7 @@ class Builder {
this.#initWorkFlows();
}

get advancedFarm() {
get workflow() {
return this.#advFarm;
}

Expand All @@ -52,10 +54,10 @@ class Builder {
* - The buyAmount in the last node is the amount to be bought
*/
translateNodesToWorkflow(
nodes: readonly SwapNode[],
initFromMode: FarmFromMode,
finalToMode: FarmToMode,
caller: string,
nodes: readonly SwapNode[],
initFromMode: FarmFromMode,
finalToMode: FarmToMode,
caller: string,
recipient: string
) {
this.#nodes = nodes;
Expand Down Expand Up @@ -83,9 +85,7 @@ class Builder {
return;
}
if (isTransferTokenNode(first)) {
this.#advFarm.add(
first.buildStep({ fromMode, toMode, recipient, copySlot: undefined })
);
this.#advFarm.add(first.buildStep({ fromMode, toMode, recipient, copySlot: undefined }));
return;
}
}
Expand All @@ -111,12 +111,26 @@ class Builder {

// No need to update Farm modes until we offload pipeline.
if (isERC20Node(node)) {
const swap = isWellNode(node)
? node.buildStep({ copySlot: this.#getPrevNodeCopySlot(i) })
: node.buildStep();

this.#advPipe.add(this.#getApproveERC20MaxAllowance(node));
this.#advPipe.add(swap, { tag: node.tag });
if (isWellNode(node)) {
this.#advPipe.add(this.#getApproveERC20MaxAllowance(node));
this.#advPipe.add(
node.buildStep({ copySlot: this.#getPrevNodeCopySlot(i) }),
{ tag: node.tag }
);
} else if (isZeroXNode(node)) {
this.#advPipe.add(this.#getApproveERC20MaxAllowance(node));
this.#advPipe.add(node.buildStep(), { tag: node.tag });
} else if (isWellSyncNode(node)) {
this.#advPipe.add(
node.transferStep({ copySlot: this.#getPrevNodeCopySlot(i) })
);
this.#advPipe.add(
node.buildStep({ recipient: Builder.sdk.contracts.pipeline.address }),
{ tag: node.tag }
);
} else {
throw new Error("Error building swap: Unknown SwapNode type.");
}
}

// Last leg of swap
Expand All @@ -127,7 +141,7 @@ class Builder {
this.#offloadPipeline(node, recipient, fromMode, finalToMode, i);
this.#advFarm.add(this.#advPipe);

// // Add UnwrapETH if last action
// Add UnwrapETH if last action
if (isUnwrapEthNode(node)) {
this.#advFarm.add(this.#getUnwrapETH(node, fromMode, i), { tag: node.tag });
}
Expand Down Expand Up @@ -163,7 +177,13 @@ class Builder {
* Adds approve Beanstalk & transferToken to advancedPipe.
* Transfer to the recipient is conditional on the recipient not being pipeline.
*/
#offloadPipeline(node: SwapNode, recipient: string, fromMode: FarmFromMode, toMode: FarmToMode, i: number) {
#offloadPipeline(
node: SwapNode,
recipient: string,
fromMode: FarmFromMode,
toMode: FarmToMode,
i: number
) {
const recipientIsPipeline =
recipient.toLowerCase() === Builder.sdk.contracts.pipeline.address.toLowerCase();
if (recipientIsPipeline) return;
Expand Down Expand Up @@ -302,6 +322,12 @@ const isWellNode = (node: SwapNode): node is WellSwapNode => {
};
const isTransferTokenNode = (node: SwapNode): node is TransferTokenNode => {
return node instanceof TransferTokenNode;
}
};
const isZeroXNode = (node: SwapNode): node is ZeroXSwapNode => {
return node instanceof ZeroXSwapNode;
};
const isWellSyncNode = (node: SwapNode): node is WellSyncSwapNode => {
return node instanceof WellSyncSwapNode;
};

export { Builder as BeanSwapBuilder };
Loading
Loading