/*
Instructions:
1. Install Sidecar and run with the environment variable for Substrate URL:
yarn global add @substrate/api-sidecar
SAS_SUBSTRATE_URL=wss://westend-ws-proxy.polka.p2p.world substrate-api-sidecar
2. Install dependencies:
yarn install
3. Run the script:
npx ts-node test.ts
*/
const API_BASE_URL = 'https://api.p2p.org';
const TOKEN = '*******';
const WS_PROVIDER_URL = 'wss://westend-ws-proxy.polka.p2p.world';
const SECRET_FILE_PATH = 'secret.json';
const TEST_PASSWORD = 'testtest';
const SIDECAR_URL = 'http://localhost:8080/transaction';
const STASH_ACCOUNT_ADDRESS =
'5GW6GmWBPhbpWPwRkria91Dn8EjTxEoqVorZxE9gkvTCHVN8';
import { readFileSync } from 'fs';
import { Keyring, WsProvider } from '@polkadot/api';
import { cryptoWaitReady } from '@polkadot/util-crypto';
import {
construct,
getRegistry,
KeyringPair,
UnsignedTransaction,
} from '@substrate/txwrapper-polkadot';
import { ApiPromise } from '@polkadot/api/cjs/bundle';
interface ApiResponse {
result: {
unsignedTransactionSerialized: string;
};
}
interface TransactionResponse {
hash: string;
}
async function getBondTx(data: any): Promise<ApiResponse> {
return fetchData(
`${API_BASE_URL}/api/v1/polkadot/westend/staking/bond`,
data
);
}
async function getBondExtraTx(data: any): Promise<ApiResponse> {
return fetchData(
`${API_BASE_URL}/api/v1/polkadot/westend/staking/bond-extra`,
data
);
}
async function fetchData(url: string, data: any): Promise<ApiResponse> {
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: 'Bearer ' + TOKEN,
},
body: JSON.stringify(data),
});
return (await response.json()) as ApiResponse;
} catch (error) {
console.error('Error fetching data:', error);
throw error;
}
}
async function fetchMetadata(): Promise<string> {
const provider = new WsProvider(WS_PROVIDER_URL);
const api = await ApiPromise.create({ provider });
const metadataRpc = await api.rpc.state.getMetadata();
await api.disconnect();
return metadataRpc.toHex();
}
function createKeypair(): KeyringPair {
const keyring = new Keyring({ type: 'sr25519' });
const keyInfo = JSON.parse(readFileSync(SECRET_FILE_PATH, 'utf8'));
const sender = keyring.addFromJson(keyInfo);
sender.decodePkcs8(TEST_PASSWORD);
return sender;
}
function signTransaction(unsignTx: string, metadataRpc: `0x{string}`): string {
const keypair = createKeypair();
const registry = getRegistry({
chainName: 'Polkadot',
specName: 'westend',
specVersion: 1018001,
metadataRpc,
});
const serialized = unsignTx;
const jsonString = Buffer.from(serialized, 'hex').toString('utf-8');
const unsigned = JSON.parse(jsonString);
const extrinsicPayload = registry.createType('ExtrinsicPayload', unsigned, {
version: unsigned.version,
});
const signature = extrinsicPayload.sign(keypair).signature;
const rawT = construct.signedTx(
unsigned as unknown as UnsignedTransaction,
signature,
{
metadataRpc,
registry,
userExtensions: { SetEvmOrigin: { payload: {}, extrinsic: {} } },
}
);
return rawT;
}
async function sendTransaction(
transactionData: string
): Promise<TransactionResponse> {
try {
const response = await fetch(SIDECAR_URL, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ tx: transactionData }),
});
return (await response.json()) as TransactionResponse;
} catch (error) {
console.error('Error sending transaction:', error);
throw error;
}
}
void (async () => {
await cryptoWaitReady();
// Get Bond Transaction
const unsignTx = await getBondTx({
stashAccountAddress: STASH_ACCOUNT_ADDRESS,
rewardDestinationType: 'account',
rewardDestination: STASH_ACCOUNT_ADDRESS,
amount: 1,
extended: true,
});
/*
// Get Bond Extra Transaction
const unsignTx = await getBondExtraTx({
stashAccountAddress: STASH_ACCOUNT_ADDRESS,
amount: 0.001,
extended: true,
});
*/
console.log('unsignTx', unsignTx);
// Fetch Metadata
const metadataRpc = await fetchMetadata();
// Sign Transaction
const signTx = signTransaction(
unsignTx.result.unsignedTransactionSerialized,
metadataRpc as `0x{string}`
);
console.log('signTx', signTx);
// Send Signed Transaction
const result = await sendTransaction(signTx);
console.log('result', result);
})();