Skip to content

Commit

Permalink
Get token list for self-sell task on Gnosis Chain (#82)
Browse files Browse the repository at this point in the history
This fixes (at least on Gnosis Chain) issue #74.

Adds support for fetching the token lists on Gnosis Chain/xdai. Api
source is blockscout, see docs
[here](https://gnosis.blockscout.com/api-docs#operations-default-get_address_token_balances).

### Test Plan

Try the script on xdai. Note the updated parameters.

```
npx hardhat self-sell --network xdai --origin 0xA03be496e67Ec29bC62F01a428683D7F9c204930 --receiver 0xA03be496e67Ec29bC62F01a428683D7F9c204930 --to-token 0xe91d153e0b41518a2ce8dd3d7944fa863463a97d --min-value 500 --leftover 100 --fee-slippage-bps 10000 --price-slippage-bps 500 --max-fee-percent 10 --validity 7200 --api-url "https://api.cow.fi/xdai"
```

<details><summary>Output</summary>

```
$ npx hardhat self-sell --network xdai --origin 0xA03be496e67Ec29bC62F01a428683D7F9c204930 --receiver 0xA03be496e67Ec29bC62F01a428683D7F9c204930 --to-token 0xe91d153e0b41518a2ce8dd3d7944fa863463a97d --min-value 10 --leftover 5 --fee-slippage-bps 10000 --price-slippage-bps 500 --max-fee-percent 10 --validity 7200 --api-url "https://api.cow.fi/xdai"
Using account 0xA03be496e67Ec29bC62F01a428683D7F9c204930
Warning: price retrieval failed for token MPS (0xfa57AA7beED63D03Aaf85fFd1753f5f6242588fb): UnsupportedToken (Token 0xfa57aa7beed63d03aaf85ffd1753f5f6242588fb is unsupported: Could not find on chain source of the token with at least 100000 balance.)
Ignored 3 units of MPS (0xfa57AA7beED63D03Aaf85fFd1753f5f6242588fb) with value 0.00 USD, does not satisfy conditions on min value and leftover
Ignored 3.428515320952721743 units of OLAS (0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f) with value 13.23 USD, does not satisfy conditions on min value and leftover
Amounts to sell:
 address                                    | value (USD) | balance         | sold amount     | symbol | buy amount (WXDAI) | fee % | needs allowance? 
--------------------------------------------+-------------+-----------------+-----------------+--------+--------------------+-------+------------------
 0x37b60f4E9A31A64cCc0024dce7D0fD07eAA0F7B3 |       26.82 |  944.4376981095 |  768.3724911123 | PNK    |      20.7225771341 | <0.01 | yes              
 0xaBEf652195F98A91E490f047A5006B71c85f058d |       37.02 |   37.2741871030 |   32.2398700605 | crvUSD |      30.4180069757 | <0.01 | yes              
 0x7eF541E2a22058048904fE5744f9c7E4C57AF717 |       56.91 |   14.4030001840 |   13.1377869560 | BAL    |      49.3235893530 | <0.01 | yes              
 0x21a42669643f45Bc0e086b8Fc2ed70c23D67509d |       94.99 | 1008.7745440583 |  955.6762529824 | FOX    |      85.4619920833 | <0.01 | yes              
 0xaf204776c7245bF4147c2612BF6e5972Ee483701 |      121.19 |  113.8535953146 |  109.1566157273 | sDAI   |     110.3879500162 | <0.01 | yes              
 0xb90D6bec20993Be5d72A5ab353343f7a0281f158 |      123.64 |    0.1002337701 |    0.0961803551 | DXD    |     112.7410064779 | <0.01 | yes              
 0x3a97704a1b25F08aa230ae53B352e2e72ef52843 |      132.82 |    2.8147454445 |    2.7087876183 | AGVE   |     117.6747378526 | <0.01 | yes              
 0xcB444e90D8198415266c6a2724b7900fb12FC56E |      241.41 |  227.4279638550 |  222.7176836210 | EURe   |     224.5954407393 | <0.01 | yes              
 0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb |      248.94 |    0.7390962527 |    0.7242516127 | GNO    |     231.7497339565 | <0.01 | yes              
 0x177127622c4A00F3d409B75571e12cB3c8973d3c |     1569.13 | 6572.5875746238 | 6551.6442848856 | COW    |    1485.9549528645 | <0.01 |                  

The settlement transaction will cost approximately 0.001110169506661017 XDAI and will create 10 orders for an estimated total value of 2602.91 USD. The proceeds of the orders will be sent to 0xA03be496e67Ec29bC62F01a428683D7F9c204930.
Submit orders to API? (y/N) 
```
</details>

Notice that the tokens that the script suggests to withdraw are
consistent with [those on
Gnosisscan](https://gnosisscan.io/tokenholdings?a=0x9008d19f58aabd9ed0d60971565aa8510560ab41)
(and the ignored tokens are next in the row).
  • Loading branch information
fedgiac authored Apr 16, 2024
1 parent 216b1c7 commit 45a0792
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 15 deletions.
15 changes: 5 additions & 10 deletions src/tasks/selfSell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -970,16 +970,11 @@ const setupSelfSellTask: () => void = () =>
}

if (tokens == undefined || tokens.length === 0) {
if (chainId === 1) {
tokens = await getTokensWithBalanceAbove(
minValue,
settlementDeployment.address,
);
} else {
throw new Error(
"Automatic token list generation is only supported on mainnet",
);
}
tokens = await getTokensWithBalanceAbove({
chainId,
settlementContract: settlementDeployment.address,
minValueUsd: parseInt(minValue),
});
}
// Exclude the toToken if needed, as we can not sell it for itself (buyToken is not allowed to equal sellToken)
tokens = tokens.filter(
Expand Down
72 changes: 67 additions & 5 deletions src/tasks/withdraw/token_balances.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,35 @@
import axios from "axios";

interface AddressInfoResponse {
export interface GetTokensInput {
minValueUsd: number;
settlementContract: string;
chainId: number;
}

export async function getTokensWithBalanceAbove({
minValueUsd,
settlementContract,
chainId,
}: GetTokensInput): Promise<string[]> {
switch (chainId) {
case 1:
return await getMainnetTokensWithBalanceAbove(
minValueUsd,
settlementContract,
);
case 100:
return await getGnosisChainTokensWithBalanceAbove(
minValueUsd,
settlementContract,
);
default:
throw new Error(
`Automatic token list generation is not supported on chain with id ${chainId}`,
);
}
}

interface EthplorerAddressInfoResponse {
tokens: {
tokenInfo: {
address: string;
Expand All @@ -13,8 +42,8 @@ interface AddressInfoResponse {
}[];
}

export async function getTokensWithBalanceAbove(
minValueUsd: string,
export async function getMainnetTokensWithBalanceAbove(
minValueUsd: number,
settlementContract: string,
): Promise<string[]> {
const response = await axios.get(
Expand All @@ -24,14 +53,47 @@ export async function getTokensWithBalanceAbove(
throw new Error(`Error getting tokens from ETHplorer ${response}`);
}
const result = [];
const data = response.data as AddressInfoResponse;
const data = response.data as EthplorerAddressInfoResponse;
for (const token of data.tokens) {
const tokenUsdValue =
token.tokenInfo.price.rate *
(token.balance / Math.pow(10, token.tokenInfo.decimals));
if (tokenUsdValue > parseInt(minValueUsd)) {
if (tokenUsdValue > minValueUsd) {
result.push(token.tokenInfo.address);
}
}
return result;
}

type BlockscoutAddressInfoResponse = BlockscoutSingleTokenInfo[];
interface BlockscoutSingleTokenInfo {
token: {
address: string;
exchange_rate: string;
decimals: string;
};
value: string;
}

export async function getGnosisChainTokensWithBalanceAbove(
minValueUsd: number,
settlementContract: string,
): Promise<string[]> {
const response = await axios.get(
`https://gnosis.blockscout.com/api/v2/addresses/${settlementContract}/token-balances`,
);
if (response.status !== 200) {
throw new Error(`Error getting tokens from ETHplorer ${response}`);
}
const result = [];
const data = response.data as BlockscoutAddressInfoResponse;
for (const { value, token } of data) {
const tokenUsdValue =
parseFloat(token.exchange_rate) *
(parseInt(value) / Math.pow(10, parseInt(token.decimals)));
if (tokenUsdValue > minValueUsd) {
result.push(token.address);
}
}
return result;
}

0 comments on commit 45a0792

Please sign in to comment.