injective-funding
Skill6 starsChanged 4 months agoApache-2.0View on GitHub
Mass fund Injective wallets with INJ, USDT, or USDC. Batch bank transfers (MsgSend), deposit to exchange subaccounts, top up taker wallets, and check balances across many wallets. Supports batching 200+ transfers in a single transaction. Also covers public-facing faucet servers that accept caller-supplied addresses in either inj1 or 0x form.
- Reads credentials
In this repository20 other files it gives its agents.
- injective-ai-cost-optimizationSkill
- injective-cliSkill
- injective-core-detect-changesSkill
- injective-docs-styleSkill
- injective-evm-developerSkill
- injective-faucetSkill
- injective-frontend-walletSkill
- injective-mcp-serversSkill
- injective-rfq-integrationsSkill
- injective-trading-accountSkill
- injective-trading-autosignSkill
- injective-trading-bridgeSkill
- injective-trading-chain-analysisSkill
- injective-trading-frontend-uxSkill
- injective-trading-market-dataSkill
- injective-trading-stakingSkill
- injective-trading-tokensSkill
- injective-usdc-integrationSkill
- injective-wallet-opsSkill
- linear-cliSkill
The file
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 74
After funding bank accounts, move to exchange subaccounts for trading:
- Very specific: line 194
Put the faucet behind a dedicated subdomain (e.g.
- Very specific: line 238
Use MsgBroadcasterWithPk.new_using_simulation() for auto gas estimation
- Very specific: line 240
Always verify with check_balances.py after funding
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Injective Mass Funding Skill
Overview
Fund many Injective wallets efficiently. Batch bank transfers, deposit to exchange subaccounts, and verify balances. Based on an Injective RFQ test framework.
Reference Code
- Seed-based funding:
scripts/fund_subaccounts.py - Two-step USDT funding:
scripts/fund_demo_subaccounts.py - Generate + fund 100 wallets:
scripts/setup_wallets.py - Top up takers:
scripts/top_up_demo.py - Bank to subaccount deposit:
scripts/move_to_sub.py - Balance checks:
scripts/check_balances.py,scripts/check_all_balances.py,scripts/check_funding.py
Batch Bank Transfers
200 Transfers in One Transaction
From setup_wallets.py — batch up to 200 MsgSend in a single tx:
from pyinjective.composer import Composer
from pyinjective.transaction import Transaction
composer = Composer(network=network.string())
msgs = []
for wallet in wallets:
# Send INJ
msgs.append(composer.msg_send(
sender=funder_address,
receiver=wallet.inj_address,
amount=inj_amount, # in wei (1e18)
denom="inj"
))
# Send USDT
msgs.append(composer.msg_send(
sender=funder_address,
receiver=wallet.inj_address,
amount=usdt_amount, # in 1e6
denom=usdt_denom
))
# Build and broadcast single tx with all msgs
tx = Transaction(msgs=msgs, ...)Quote-asset denoms by network
| Network | Asset | Denom | Decimals |
| Mainnet | USDT | peggy0xdAC17F958D2ee523a2206206994597C13D831ec7 | 6 |
| Mainnet | USDC | peggy0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 | 6 |
| Testnet | USDT | peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5 | 6 |
| Testnet | USDC | erc20:0x0C382e685bbeeFE5d3d9C29e29E341fEE8E84C5d | 6 |
| Devnet | varies per config | — | 6 |
Which asset a given RFQ/exchange market uses depends on its quote_denom. Query the market via LCD (/injective/exchange/v2/derivative/markets/{id}) and look at market.quote_denom — never hardcode.
INJ Amounts
INJ uses 18 decimals (like ETH wei):
- 1 INJ =
1000000000000000000(1e18) - 0.1 INJ =
100000000000000000(1e17)
Subaccount Deposits
After funding bank accounts, move to exchange subaccounts for trading:
from pyinjective.core.broadcaster import MsgBroadcasterWithPk
msg = composer.msg_subaccount_deposit(
sender=address,
subaccount_id=get_subaccount_id(address, nonce=0),
amount=Decimal(str(amount)),
denom=denom
)
broadcaster = MsgBroadcasterWithPk.new_using_simulation(
network=network,
private_key=private_key
)
await broadcaster.broadcast([msg])Subaccount ID Calculation
def get_subaccount_id(inj_address: str, nonce: int = 0) -> str:
"""Generate subaccount ID: eth_address + nonce (padded to 24 hex chars)"""
eth_addr = inj_to_eth_address(inj_address)
return eth_addr.lower() + format(nonce, '024x')Two-Step USDT Funding (Testnet Pattern)
- Parent sends USDT to child via bank
MsgSend - Child deposits USDT from bank to exchange subaccount
This is needed because on testnet, the public faucet only gives INJ, not USDT/USDC. You need to pre-fund a parent wallet with the quote asset.
Public Faucet Server (Accept-Either-Address Pattern)
Pattern for a browser-facing faucet that takes a caller-supplied address and sends INJ + quote-asset in a single MsgSend. Proven working against testnet INJ/USDC PERP (2026-04-20; tx EDE9788844E1780DC72ABBC67346550A85E842A57BF9A451FDDD130125B98163).
Principles
- Accept both
inj1…and0x…— same secp256k1 key, two encodings. MetaMask/bridge users only have the0xform; Cosmos-native users pasteinj1. Forcing one side creates a support tax. See injective-wallet-ops for the conversion helpers. - Canonicalize to
inj1at the boundary.MsgSendwants bech32 receivers; more importantly, rate limits + dedup keys must be keyed on the canonical form. Otherwise a caller can flip encodings to dodge a cooldown. - Per-request hard caps (e.g. 500 USDC + 2 INJ) enforced server-side, not trusted from the request body.
- Per-address cooldown in a file-backed map (
{ addr: { lastRequestAt, lastTxHash } }). SQLite is overkill for testnet volumes. - Status endpoint that returns hot-wallet balance + remaining budget. Useful for dashboards and for
npm run dev-time debugging; optional to expose publicly. - Fresh hot wallet per deployment, not reused across envs. Fund it with just enough for ~100 requests so a leak is a bounded loss. Top up on demand.
Reference implementation (Node + @injectivelabs/sdk-ts)
import 'dotenv/config';
import express from 'express';
import {
PrivateKey, MsgSend, MsgBroadcasterWithPk,
getInjectiveAddress, getEthereumAddress,
} from '@injectivelabs/sdk-ts';
import { Network } from '@injectivelabs/networks';
const FAUCET_PK = process.env.FAUCET_PRIVATE_KEY;
const USDC_DENOM = 'erc20:0x0C382e685bbeeFE5d3d9C29e29E341fEE8E84C5d'; // testnet
const USDC_SUB = (500n * 10n**6n ).toString(); // 500 USDC
const INJ_SUB = (2n * 10n**18n).toString(); // 2 INJ
const COOLDOWN_MS = 24 * 60 * 60 * 1000;
const priv = PrivateKey.fromHex('0x' + FAUCET_PK.replace(/^0x/, ''));
const FAUCET_ADDR = priv.toBech32();
const broadcaster = new MsgBroadcasterWithPk({ privateKey: FAUCET_PK, network: Network.Testnet });
// 1. Accept either form, canonicalize to inj1 for downstream use
const INJ_BECH32 = /^inj1[02-9ac-hj-np-z]{38}$/;
const ETH_HEX = /^0x[0-9a-fA-F]{40}$/;
function normalize(raw) {
const s = (raw || '').trim();
if (INJ_BECH32.test(s)) return { inj: s, eth: getEthereumAddress(s).toLowerCase() };
if (ETH_HEX.test(s)) return { inj: getInjectiveAddress(s), eth: s.toLowerCase() };
throw new Error('malformed address — expected inj1… (43 chars) or 0x… (42 chars)');
}
// 2. File-backed rate limit, keyed on the inj form
const limits = new Map(); // load from disk on boot, persist on each record
// 3. The send itself — a single MsgSend with both denoms in one tx
async function sendFunds(recipientInj) {
const msg = MsgSend.fromJSON({
srcInjectiveAddress: FAUCET_ADDR,
dstInjectiveAddress: recipientInj,
amount: [{ denom: USDC_DENOM, amount: USDC_SUB }, { denom: 'inj', amount: INJ_SUB }],
});
const tx = await broadcaster.broadcast({ msgs: [msg] });
return tx.txHash;
}
const app = express();
app.use(express.json());
app.post('/api/faucet', async (req, res) => {
let parsed;
try { parsed = normalize(req.body?.address); }
catch (e) { return res.status(400).json({ error: e.message }); }
const prior = limits.get(parsed.inj);
if (prior && Date.now() - prior.lastRequestAt < COOLDOWN_MS) {
return res.status(429).json({ error: 'cooldown', retry_after_seconds: /* … */ 0 });
}
try {
const txHash = await sendFunds(parsed.inj);
limits.set(parsed.inj, { lastRequestAt: Date.now(), lastTxHash: txHash });
// persist limits to disk
res.json({ ok: true, tx_hash: txHash, recipient: parsed });
} catch (e) {
res.status(500).json({ error: e.message || 'send failed' });
}
});
app.listen(46001);Gotchas
- First send to an empty account may log "account not found" before the tx completes — normal, the first
MsgSendcreates the bank account on-chain. - INJ gas is the tighter budget than USDC. A 50k-USDC / 100-INJ seed gives ~50 requests (100 INJ / 2 per req) before the INJ side drains even though USDC is nowhere near empty. Monitor both.
- Expose the status endpoint behind the same basic-auth / cookie gate as the rest of your site if you don't want randos hitting it from the open internet.
- Put the faucet behind a dedicated subdomain (e.g.
faucet.example.xyz) or a/api/faucetnginxlocationblock on an existing domain — a separate pm2 process, not insidepm2 serve(which is static-only).
Balance Checks
Bank Balance
balance = await client.fetch_bank_balance(address, denom)
# Returns {"balance": {"denom": "...", "amount": "..."}}Subaccount Balance
deposits = await client.fetch_subaccount_deposits(subaccount_id)
# Returns available + total deposits per denomFunding Verification Script
# Check if all wallets are properly funded
python scripts/check_funding.py
# Shows: funder balance, per-wallet bank balance, per-wallet subaccount balanceCLI Patterns
# Fund 20 MM + 80 retail from seed phrases (devnet)
python scripts/fund_subaccounts.py --env devnet0 --count 20 --amount 10
# Fund only MMs
python scripts/fund_subaccounts.py --env testnet --count 20 --mm-only --amount 5
# Generate 100 fresh wallets + fund them
python scripts/setup_wallets.py
# Move all bank USDT to subaccounts
python scripts/move_to_sub.py
# Top up 10 retail takers with 100 USDT each
python scripts/top_up_demo.pyKey Facts
- Max ~200 msgs per transaction (gas limit)
- Use
MsgBroadcasterWithPk.new_using_simulation()for auto gas estimation - Sequence mismatch errors are common in rapid-fire txs -- add retry logic
- Always verify with
check_balances.pyafter funding - Funder wallet needs enough INJ for gas + enough tokens for all transfers
Dependencies
Python (mass funding, batch MsgSend, subaccount deposits):
injective-py>=1.12.0httpx>=0.27- Python >= 3.11
Node (public faucet server + browser-origin address inputs):
@injectivelabs/sdk-ts—PrivateKey,MsgSend,MsgBroadcasterWithPk,getInjectiveAddress,getEthereumAddress@injectivelabs/networks—Network.Testnet/Network.Mainnetexpress,dotenv- Node >= 20
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

