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 17 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
91 changes: 91 additions & 0 deletions examples/exit/weighted.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
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";
import anvilGlobalSetup from "../../test/anvil/anvil-global-setup";

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 testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig
const slippage = Slippage.fromPercentage('1'); // 1%

const exit = async () => {
await anvilGlobalSetup();
const balancerApi = new BalancerApi(balancerApiUrl, 1);
const poolState: PoolStateInput = await balancerApi.pools.fetchPoolState(poolId);
const client: Client & PublicActions & TestActions & WalletActions = createTestClient({
mode: 'anvil',
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),
]
);

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 { config } from '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";
import anvilGlobalSetup from "../../test/anvil/anvil-global-setup";

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 testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig
const slippage = Slippage.fromPercentage('1'); // 1%

const join = async () => {
await anvilGlobalSetup();
const balancerApi = new BalancerApi(balancerApiUrl, 1);
const poolState: PoolStateInput = await balancerApi.pools.fetchPoolState(poolId);
const client: Client & PublicActions & TestActions & WalletActions = createTestClient({
mode: 'anvil',
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),
],
);


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}`);
return;
}

join().then(()=>{});
13 changes: 10 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,18 @@
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"typings": "dist/index.d.ts",
"files": ["dist/"],
"files": [
"dist/"
],
"scripts": {
"build": "tsup",
"format": "rome format .",
"lint": "rome check .",
"test": "vitest dev",
"test:ci": "vitest run",
"changeset": "changeset",
"changeset:release": "pnpm build && changeset publish"
"changeset:release": "pnpm build && changeset publish",
"example": "npx ts-node -P tsconfig.testing.json -r tsconfig-paths/register"
},
"dependencies": {
"async-retry": "^1.3.3",
Expand All @@ -39,10 +42,14 @@
"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",
"vitest": "^0.34.6"
},
"packageManager": "^[email protected]"
"packageManager": "^[email protected]",
"resolutions": {
johngrantuk marked this conversation as resolved.
Show resolved Hide resolved
"get-port": "5.1.1"
}
}
29 changes: 25 additions & 4 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';
Loading
Loading