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

Creating a Balancer API provider to fetch pool state for join txs; #97

Merged
merged 20 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from 10 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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,5 @@ typechain-types
cache
artifacts

#IDE
.idea
82 changes: 82 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,85 @@ If your platform does not support one of the required features, it is also possi
Testing requires access to an archive node for onchain quote comparisons. This can be done using Infura.

`pnpm test`

## Balancer Api Provider

The Balancer API Provider is a provider that facilitates
data fetching from the Balancer API,
it can be used for:
- Fetch Pool State for Joins;
- Fetch Pool State for Exits.

### Usage for Joining Pool

```ts
import { BalancerApi, PoolJoin } from "@balancer/sdk";
...
const joinInput: ProportionalJoinInput = {
bptOut,
chainId,
rpcUrl,
kind: JoinKind.Proportional,
};

const balancerApi = new BalancerApi('https://backend-v3-canary.beets-ftm-node.com/graphql', 1);
const poolState = await balancerApi.pools.fetchPoolState('0x5f1d6874cb1e7156e79a7563d2b61c6cbce03150000200000000000000000586');
const poolJoin = new PoolJoin();
const queryResult = await poolJoin.query(joinInput, poolState);
const { call, to, value, maxAmountsIn, minBptOut } =
poolJoin.buildCall({
...queryResult,
slippage,
sender: signerAddress,
recipient: signerAddress,
});
const client = createClient({
...
})

await client.sendTransaction({
account: signerAddress,
chain: client.chain,
data: call,
to,
value,
});
```
Full working join example: [examples/join/weighted.ts](./examples/join/weighted.ts)

### Usage for Exiting Pool
```ts
import { BalancerApi, PoolExit } from "@balancer/sdk";
...
const exitInput: SingleAssetExitInput = {
chainId,
rpcUrl,
bptIn,
tokenOut,
kind: ExitKind.SINGLE_ASSET,
};

const balancerApi = new BalancerApi('https://backend-v3-canary.beets-ftm-node.com/graphql', 1);
const poolState = await balancerApi.pools.fetchPoolState('0x5f1d6874cb1e7156e79a7563d2b61c6cbce03150000200000000000000000586');
const poolExit = new PoolExit();
const queryResult = await poolExit.query(exitInput, poolState);
const { call, to, value, maxAmountsIn, minBptOut } =
poolExit.buildCall({
...queryResult,
slippage,
sender: signerAddress,
recipient: signerAddress,
});
const client = createClient({
...
})

await client.sendTransaction({
account: signerAddress,
chain: client.chain,
data: call,
to,
value,
});
```
Full working exit example: [examples/exit/weighted.ts](./examples/exit/weighted.ts)
92 changes: 92 additions & 0 deletions examples/exit/weighted.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import dotenv from 'dotenv';
dotenv.config();

import { BalancerApi } from "../../src/data/providers/balancer-api";
import {
ChainId,
CHAINS, ExitKind, PoolExit, PoolStateInput,
SingleAssetExitInput,
Slippage,
Token,
TokenAmount,
} from "../../src";
import {
Client,
createTestClient,
http,
parseUnits,
PublicActions,
publicActions,
TestActions, WalletActions,
walletActions
} from "viem";
import { forkSetup, sendTransactionGetBalances } from "../../test/lib/utils/helper";

const balancerApiUrl = 'https://backend-v3-canary.beets-ftm-node.com/graphql';
const poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH
const chainId = ChainId.MAINNET;
const rpcUrl = 'http://127.0.0.1:8545/';
const blockNumber = BigInt(18043296);
const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig
const slippage = Slippage.fromPercentage('1'); // 1%

const exit = async () => {
const balancerApi = new BalancerApi(balancerApiUrl, 1);
const poolState: PoolStateInput = await balancerApi.pools.fetchPoolState(poolId);
const client: Client & PublicActions & TestActions & WalletActions = createTestClient({
mode: 'hardhat',
chain: CHAINS[chainId],
transport: http(rpcUrl),
})
.extend(publicActions)
.extend(walletActions);
const bpt = new Token(chainId, poolState.address, 18, 'BPT');

await forkSetup(
client,
testAddress,
[poolState.address],
[0],
[
parseUnits('100', 18),
],
process.env.ETHEREUM_RPC_URL as string,
blockNumber,
);

const bptIn = TokenAmount.fromHumanAmount(bpt, '1');
const tokenOut = '0xba100000625a3754423978a60c9317c58a424e3D'; // BAL

const poolExit = new PoolExit();

const exitInput: SingleAssetExitInput = {
chainId,
rpcUrl,
bptIn,
tokenOut,
kind: ExitKind.SINGLE_ASSET,
};

const queryResult = await poolExit.query(exitInput, poolState);

const { call, to, value } =
poolExit.buildCall({
...queryResult,
slippage,
sender: testAddress,
recipient: testAddress,
});
const { transactionReceipt, balanceDeltas } =
await sendTransactionGetBalances(
[...poolState.tokens.map(({address})=>address), bpt.address],
client,
testAddress,
to,
call,
value,
);
console.log(`transaction status: ${transactionReceipt.status}`);
console.log(`token amounts deltas per token: ${balanceDeltas}`);
}

exit();
98 changes: 98 additions & 0 deletions examples/join/weighted.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import dotenv from 'dotenv';
dotenv.config();

import {
BalancerApi,
ChainId,
CHAINS,
JoinKind,
PoolJoin, PoolStateInput,
Slippage,
Token,
TokenAmount,
UnbalancedJoinInput
} from "../../src";
import {
Client,
createTestClient,
http,
parseUnits,
PublicActions,
publicActions,
TestActions, WalletActions,
walletActions
} from "viem";
import { forkSetup, sendTransactionGetBalances } from "../../test/lib/utils/helper";

const balancerApiUrl = 'https://backend-v3-canary.beets-ftm-node.com/graphql';
const poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH
const chainId = ChainId.MAINNET;
const rpcUrl = 'http://127.0.0.1:8545/';
const blockNumber = BigInt(18043296);
const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig
const slippage = Slippage.fromPercentage('1'); // 1%

const join = async () => {
const balancerApi = new BalancerApi(balancerApiUrl, 1);
const poolState: PoolStateInput = await balancerApi.pools.fetchPoolState(poolId);
const client: Client & PublicActions & TestActions & WalletActions = createTestClient({
mode: 'hardhat',
chain: CHAINS[chainId],
transport: http(rpcUrl),
})
.extend(publicActions)
.extend(walletActions);

await forkSetup(
client,
testAddress,
[...poolState.tokens.map((t) => t.address), poolState.address],
[1,3,0],
[
...poolState.tokens.map((t) => parseUnits('100', t.decimals)),
parseUnits('100', 18),
],
process.env.ETHEREUM_RPC_URL as string,
blockNumber,
);


const poolJoin = new PoolJoin();
const poolTokens = poolState.tokens.map(
(t) => new Token(chainId, t.address, t.decimals),
);
const amountsIn = poolTokens.map((t) =>
TokenAmount.fromHumanAmount(t, '1'),
);

// perform join query to get expected bpt out
const joinInput: UnbalancedJoinInput = {
amountsIn,
chainId,
rpcUrl,
kind: JoinKind.Unbalanced,
};

const queryResult = await poolJoin.query(joinInput, poolState);

const { call, to, value } =
poolJoin.buildCall({
...queryResult,
slippage,
sender: testAddress,
recipient: testAddress,
});
const { transactionReceipt, balanceDeltas } =
await sendTransactionGetBalances(
[...poolState.tokens.map(({address})=>address), poolState.address /*BPT*/],
client,
testAddress,
to,
call,
value,
);
console.log(`transaction status: ${transactionReceipt.status}`);
console.log(`token amounts deltas per token: ${balanceDeltas}`);
}

join();
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"test:ci": "vitest run",
"changeset": "changeset",
"changeset:release": "pnpm build && changeset publish",
"node": "npx hardhat node --tsconfig tsconfig.testing.json --fork $(. ./.env && echo $ETHEREUM_RPC_URL)"
"node": "npx hardhat node --tsconfig tsconfig.testing.json --fork $(. ./.env && echo $ETHEREUM_RPC_URL)",
"example": "npx ts-node -P tsconfig.testing.json -r tsconfig-paths/register"
},
"dependencies": {
"async-retry": "^1.3.3",
Expand All @@ -43,6 +44,7 @@
"pino-pretty": "^10.0.0",
"rome": "12.1.3",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.2.0",
johngrantuk marked this conversation as resolved.
Show resolved Hide resolved
"tsup": "^6.6.0",
"typescript": "^5.0.4",
"vite": "^4.4.2",
Expand Down
1 change: 1 addition & 0 deletions src/data/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './enrichers/onChainPoolDataEnricher';
export * from './providers/subgraphPoolProvider';
export * from './providers/balancer-api';
export * from './types';
22 changes: 22 additions & 0 deletions src/data/providers/balancer-api/client/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export class BalancerApiClient {
apiUrl: string;
chainId: number;
johngrantuk marked this conversation as resolved.
Show resolved Hide resolved
constructor(apiUrl: string, chainId: number) {
this.apiUrl = apiUrl;
this.chainId = chainId;
}

async fetch(operationName: string, query: any, variables: any) {
johngrantuk marked this conversation as resolved.
Show resolved Hide resolved
const requestQuery = {
operationName,
query,
variables,
};
const response = await fetch(this.apiUrl, {
method: 'post',
body: JSON.stringify(requestQuery),
headers: { 'Content-Type': 'application/json', ChainId: this.chainId.toString() },
});
return response.json();
}
}
16 changes: 16 additions & 0 deletions src/data/providers/balancer-api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Pools } from "./modules/pool-state";
import { BalancerApiClient } from "./client";

export class BalancerApi {

balancerApiClient: BalancerApiClient;
pools: Pools;


constructor(balancerApiUrl: string, chainId: number){
this.balancerApiClient = new BalancerApiClient(balancerApiUrl, chainId);
this.pools = new Pools(this.balancerApiClient);
}


}
Loading
Loading