feat: add broadcastSponsoredTx

This commit is contained in:
Kyle Fang
2023-08-03 16:45:47 +08:00
parent a821a999bb
commit 02d1c0e4a7
4 changed files with 104 additions and 0 deletions

View File

@@ -91,6 +91,13 @@ Check if a transaction is a swap transaction from Alex
function isAlexSwapTransaction(deployer: string, contractName: string, functionName: string): boolean;
```
### broadcastSponsoredTx
Broadcast a sponsored transaction to Alex's sponsored tx services
```javascript
function broadcastSponsoredTx(txRaw: string): Promise<string>;
````
## Installation
You can install Alex-SDK using npm:

View File

@@ -12,6 +12,7 @@ import {
import { fetchLatestPrices } from './utils/currencyPrice';
import { assignConfig, AssignConfigParams, configs } from './config';
import { AlexContracts } from './generated/smartContract/contracts_Alex';
import { broadcastSponsoredTx } from './utils/sponsoredTx';
export class AlexSDK {
static configure(config: AssignConfigParams) {
@@ -96,4 +97,8 @@ export class AlexSDK {
// @ts-ignore
return AlexContracts[contractName][functionName] != null;
}
broadcastSponsoredTx(txRaw: string): Promise<string> {
return broadcastSponsoredTx(txRaw);
}
}

View File

@@ -3,6 +3,7 @@ import { Currency } from './currency';
const CONTRACT_DEPLOYER = 'SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9';
const API_HOST = 'https://stacks-blockchain-lb.alexlab.co';
const IS_MAINNET = true;
const SPONSORED_TX_EXECUTOR = 'https://sponsor-tx.alexlab.co/v1/graphql';
const NATIVE_TOKEN_MAPPING: {
[P in Exclude<Currency, Currency.STX>]: {
@@ -84,6 +85,7 @@ export const configs = {
CONTRACT_DEPLOYER,
API_HOST,
NATIVE_TOKEN_MAPPING,
SPONSORED_TX_EXECUTOR,
};
type AlexConfig = typeof configs;

90
src/utils/sponsoredTx.ts Normal file
View File

@@ -0,0 +1,90 @@
import { configs } from '../config';
export async function broadcastSponsoredTx(tx: string): Promise<string> {
const response = await fetch(configs.SPONSORED_TX_EXECUTOR, {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: `mutation run($tx:String!) {
execute(tx:$tx)
}`,
variables: {
tx,
},
}),
});
if (!response.ok) {
throw new Error(response.statusText);
}
const result = await response.json();
if (result.data == null) {
throw new Error(result.errors?.[0]?.message ?? 'Unknown Error');
}
const {
data: { execute: txId },
} = result;
return await retry(() => tryToFetchTxId(txId), 10);
}
async function tryToFetchTxId(txId: string): Promise<string | null> {
const response = await fetch(configs.SPONSORED_TX_EXECUTOR, {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: `query checkStatus($txId: bytea!) {
user_operations(where:{tx_id:{_eq:$txId}}) {
sponsor_tx_id
error
}
}`,
variables: {
txId: hexAddressToHasuraAddress(txId),
},
}),
});
if (!response.ok) {
throw new Error(response.statusText);
}
const {
data: { user_operations },
} = await response.json();
if (user_operations.length === 0) {
return null;
}
const operation = user_operations[0];
if (operation.error) {
throw new Error(operation.error);
} else if (operation.sponsor_tx_id) {
return hasuraAddressToHex(operation.sponsor_tx_id);
}
return null;
}
async function retry<T>(
action: () => Promise<T | null>,
count = 10
): Promise<T> {
for (let i = 0; i < count; i++) {
const result = await action();
if (result === null) {
await new Promise((resolve) => setTimeout(resolve, 1000));
continue;
}
return result;
}
throw new Error('Timeout waiting for response');
}
export function hexAddressToHasuraAddress(input: string): string {
return '\\x' + input;
}
export function hasuraAddressToHex(input: string): string {
return input.replace(/\\x/, '0x').toLowerCase();
}