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 14 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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,3 @@ artifacts

#IDE
.idea

82 changes: 82 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,88 @@ Testing requires access to an archive node for onchain quote comparisons. This c

`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)

## Anvil client
To download and install the anvil client, run the following commands (MacOS):
- `curl -L https://foundry.paradigm.xyz | bash`
Expand Down
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();
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,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
18 changes: 18 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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';
31 changes: 31 additions & 0 deletions src/data/providers/balancer-api/client/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { ChainId } from '../../../../utils';

export class BalancerApiClient {
apiUrl: string;
chainId: ChainId;
constructor(apiUrl: string, chainId: number) {
johngrantuk marked this conversation as resolved.
Show resolved Hide resolved
this.apiUrl = apiUrl;
this.chainId = chainId;
}

async fetch(
johngrantuk marked this conversation as resolved.
Show resolved Hide resolved
operationName: string,
query: string,
variables: { id: string },
) {
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