Integration Guide
Integrate taoflash relay into your application to protect transactions from MEV attacks.
Flow
Each step is independent — implement signing however you want (browser wallet, backend keypair, hardware wallet).
Step 1: Prepare
Request transaction parameters from the relay.
- curl
- TypeScript
- Python
- Go
- HTTP
curl -X POST https://relay.taoflash.com/prepare \
-H "Content-Type: application/json" \
-d '{
"address": "5FKuRKBzdjNdFuivUNScGgc9i7xBLeMrMVAqdBT7fpULRfx8",
"call_module": "subtensorModule",
"call_function": "addStakeLimit",
"call_params": {
"hotkey": "5Ecr9sKq81UqBfXHpcMvyv8NBikujwJ5yGSPzskqRVLrAdkL",
"netuid": 1,
"amount_staked": 1000000000,
"limit_price": 999999999999,
"allow_partial": false
}
}'
const response = await fetch('https://relay.taoflash.com/prepare', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
address: '5FKuRKBzdjNdFuivUNScGgc9i7xBLeMrMVAqdBT7fpULRfx8',
call_module: 'subtensorModule',
call_function: 'addStakeLimit',
call_params: {
hotkey: '5Ecr9sKq81UqBfXHpcMvyv8NBikujwJ5yGSPzskqRVLrAdkL',
netuid: 1,
amount_staked: 1000000000,
limit_price: 999999999999,
allow_partial: false
}
})
})
const prepareData = await response.json()
import requests
response = requests.post('https://relay.taoflash.com/prepare', json={
'address': '5FKuRKBzdjNdFuivUNScGgc9i7xBLeMrMVAqdBT7fpULRfx8',
'call_module': 'subtensorModule',
'call_function': 'addStakeLimit',
'call_params': {
'hotkey': '5Ecr9sKq81UqBfXHpcMvyv8NBikujwJ5yGSPzskqRVLrAdkL',
'netuid': 1,
'amount_staked': 1000000000,
'limit_price': 999999999999,
'allow_partial': False
}
})
prepare_data = response.json()
package main
import (
"bytes"
"encoding/json"
"net/http"
)
type PrepareRequest struct {
Address string `json:"address"`
CallModule string `json:"call_module"`
CallFunction string `json:"call_function"`
CallParams map[string]interface{} `json:"call_params"`
}
func prepare() (*http.Response, error) {
req := PrepareRequest{
Address: "5FKuRKBzdjNdFuivUNScGgc9i7xBLeMrMVAqdBT7fpULRfx8",
CallModule: "subtensorModule",
CallFunction: "addStakeLimit",
CallParams: map[string]interface{}{
"hotkey": "5Ecr9sKq81UqBfXHpcMvyv8NBikujwJ5yGSPzskqRVLrAdkL",
"netuid": 1,
"amount_staked": 1000000000,
"limit_price": 999999999999,
"allow_partial": false,
},
}
body, _ := json.Marshal(req)
return http.Post(
"https://relay.taoflash.com/prepare",
"application/json",
bytes.NewBuffer(body),
)
}
POST /prepare HTTP/1.1
Host: relay.taoflash.com
Content-Type: application/json
{
"address": "5FKuRKBzdjNdFuivUNScGgc9i7xBLeMrMVAqdBT7fpULRfx8",
"call_module": "subtensorModule",
"call_function": "addStakeLimit",
"call_params": {
"hotkey": "5Ecr9sKq81UqBfXHpcMvyv8NBikujwJ5yGSPzskqRVLrAdkL",
"netuid": 1,
"amount_staked": 1000000000,
"limit_price": 999999999999,
"allow_partial": false
}
}
Response:
{
"call_module": "subtensorModule",
"call_function": "addStakeLimit",
"call_params": { ... },
"era": "0x4503",
"nonce": "0x0c",
"blockHash": "0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"
}
Step 2: Sign
Sign using the parameters from Step 1. This step depends on your wallet implementation.
Browser Wallet (Polkadot.js)
import { web3FromSource } from '@polkadot/extension-dapp'
const injector = await web3FromSource(account.meta.source)
const params = Object.values(prepareData.call_params)
const extrinsic = api.tx[prepareData.call_module][prepareData.call_function](...params)
const signedExtrinsic = await extrinsic.signAsync(account.address, {
signer: injector.signer,
era: prepareData.era,
nonce: prepareData.nonce,
blockHash: prepareData.blockHash
})
const signedHex = signedExtrinsic.toHex()
Backend Keypair (TypeScript)
import { Keyring } from '@polkadot/keyring'
const keyring = new Keyring({ type: 'sr25519' })
const pair = keyring.addFromUri(process.env.MNEMONIC)
const params = Object.values(prepareData.call_params)
const extrinsic = api.tx[prepareData.call_module][prepareData.call_function](...params)
const signedExtrinsic = extrinsic.sign(pair, {
era: prepareData.era,
nonce: prepareData.nonce,
blockHash: prepareData.blockHash
})
const signedHex = signedExtrinsic.toHex()
Backend Keypair (Python)
Using py-substrate-interface:
from substrateinterface import SubstrateInterface, Keypair
# Connect to Bittensor
substrate = SubstrateInterface(
url="wss://entrypoint-finney.opentensor.ai:443"
)
# Create keypair from mnemonic
keypair = Keypair.create_from_mnemonic("your mnemonic here")
# Compose the call
call = substrate.compose_call(
call_module=prepare_data['call_module'],
call_function=prepare_data['call_function'],
call_params=prepare_data['call_params']
)
# Create signed extrinsic with relay parameters
extrinsic = substrate.create_signed_extrinsic(
call=call,
keypair=keypair,
era=prepare_data['era'],
nonce=int(prepare_data['nonce'], 16) if isinstance(prepare_data['nonce'], str) else prepare_data['nonce']
)
# Get the signed hex
signed_hex = '0x' + extrinsic.data.hex()
Step 3: Queue
Submit the signed transaction to the relay.
- curl
- TypeScript
- Python
- Go
- HTTP
curl -X POST https://relay.taoflash.com/queue \
-H "Content-Type: application/json" \
-d '{"signed_extrinsic": "0x45028400..."}'
const response = await fetch('https://relay.taoflash.com/queue', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ signed_extrinsic: signedHex })
})
const { tx_id } = await response.json()
response = requests.post('https://relay.taoflash.com/queue', json={
'signed_extrinsic': signed_hex
})
tx_id = response.json()['tx_id']
type QueueRequest struct {
SignedExtrinsic string `json:"signed_extrinsic"`
}
func queue(signedHex string) (*http.Response, error) {
req := QueueRequest{SignedExtrinsic: signedHex}
body, _ := json.Marshal(req)
return http.Post(
"https://relay.taoflash.com/queue",
"application/json",
bytes.NewBuffer(body),
)
}
POST /queue HTTP/1.1
Host: relay.taoflash.com
Content-Type: application/json
{"signed_extrinsic": "0x45028400..."}
Response:
{
"tx_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
Step 4: Poll Status
Poll until the transaction is confirmed or failed.
- curl
- TypeScript
- Python
- Go
- HTTP
curl https://relay.taoflash.com/status/a1b2c3d4-e5f6-7890-abcd-ef1234567890
async function waitForConfirmation(txId: string) {
const maxAttempts = 30
for (let i = 0; i < maxAttempts; i++) {
await new Promise(r => setTimeout(r, 2000))
const response = await fetch(`https://relay.taoflash.com/status/${txId}`)
const status = await response.json()
if (status.status === 'confirmed') {
return { success: true, hash: status.extrinsic_hash }
}
if (status.status === 'failed') {
return { success: false, error: status.error }
}
}
throw new Error('Timeout')
}
import time
def wait_for_confirmation(tx_id: str, max_attempts: int = 30):
for _ in range(max_attempts):
time.sleep(2)
response = requests.get(f'https://relay.taoflash.com/status/{tx_id}')
status = response.json()
if status['status'] == 'confirmed':
return {'success': True, 'hash': status['extrinsic_hash']}
if status['status'] == 'failed':
return {'success': False, 'error': status['error']}
raise TimeoutError('Transaction confirmation timeout')
func waitForConfirmation(txID string) (map[string]interface{}, error) {
for i := 0; i < 30; i++ {
time.Sleep(2 * time.Second)
resp, err := http.Get("https://relay.taoflash.com/status/" + txID)
if err != nil {
continue
}
var status map[string]interface{}
json.NewDecoder(resp.Body).Decode(&status)
if status["status"] == "confirmed" {
return status, nil
}
if status["status"] == "failed" {
return status, fmt.Errorf("transaction failed")
}
}
return nil, fmt.Errorf("timeout")
}
GET /status/a1b2c3d4-e5f6-7890-abcd-ef1234567890 HTTP/1.1
Host: relay.taoflash.com
Transaction States
| State | Description |
|---|---|
queued | Waiting for optimal submission block |
submitted | Submitted to Bittensor network |
confirmed | Included in block and validated |
failed | Transaction failed |
Error Handling
Transaction Failed
Check status.error and status.validation for details:
if (status.status === 'failed') {
const error = status.validation?.error || status.error
console.error('Transaction failed:', error)
}
Compatible Wallets
Any Polkadot-compatible wallet works with taoflash relay:
- Talisman - Recommended for best UX
- Bittensor Wallet - Official Bittensor wallet
- Polkadot.js Extension - Developer favorite
- SubWallet - Mobile-friendly option
Notes
- Parameter names can use either
camelCaseorsnake_case - No authentication required for relay endpoints
- See Supported Transactions for all available transaction types