Skip to content

Commit 1e43b21

Browse files
author
konrad-w3
committed
feat: api v2 examples
1 parent 2c493ac commit 1e43b21

2 files changed

Lines changed: 235 additions & 199 deletions

File tree

src/evm/index.ts

Lines changed: 134 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,146 @@
1-
import axios from 'axios';
2-
import { createPublicClient, createWalletClient, http } from 'viem';
1+
import { createWalletClient, createPublicClient, http, type TypedDataDefinition } from 'viem';
32
import { privateKeyToAccount } from 'viem/accounts';
4-
import { mainnet } from 'viem/chains';
3+
import { optimism } from 'viem/chains';
54
import * as dotenv from 'dotenv';
65
dotenv.config();
76

8-
// Replace with your actual private key - NEVER hardcode in production code
9-
const PRIVATE_KEY = process.env.EVM_PRIVATE_KEY as `0x${string}`
7+
const API = 'https://stargate.finance/api/unstable';
8+
const API_KEY = process.env.STARGATE_API_KEY!;
9+
const PRIVATE_KEY = process.env.EVM_PRIVATE_KEY as `0x${string}`;
1010
const account = privateKeyToAccount(PRIVATE_KEY);
11+
const wallet = createWalletClient({ account, chain: optimism, transport: http() });
12+
const client = createPublicClient({ chain: optimism, transport: http() });
1113

12-
// Initialize clients
13-
const ethereumClient = createPublicClient({
14-
chain: mainnet,
15-
transport: http()
16-
});
17-
18-
const walletClient = createWalletClient({
19-
account,
20-
chain: mainnet,
21-
transport: http()
22-
});
23-
24-
async function fetchStargateRoutes() {
25-
try {
26-
// Fetching route for USDC transfer from Ethereum to Polygon - https://docs.stargate.finance
27-
const response = await axios.get('https://stargate.finance/api/v1/quotes', {
28-
params: {
29-
srcToken: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC on Ethereum
30-
dstToken: '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359', // USDC on Polygon
31-
srcAddress: '0x0C0d18aa99B02946C70EAC6d47b8009b993c9BfF',
32-
dstAddress: '0x0C0d18aa99B02946C70EAC6d47b8009b993c9BfF',
33-
srcChainKey: 'ethereum', // All chainKeys - https://stargate.finance/api/v1/chains
34-
dstChainKey: 'polygon',
35-
srcAmount: '1000000', // 1 USDC (6 decimals)
36-
dstAmountMin: '900000' // Amount to receive deducted by Stargate fees (max 0.15%)
37-
}
38-
});
39-
40-
console.log('Stargate quotes data:', response.data);
41-
return response.data;
42-
} catch (error) {
43-
if (axios.isAxiosError(error)) {
44-
console.error('Axios error:', error.message);
45-
if (error.response) {
46-
console.error('Response data:', error.response.data);
47-
}
48-
} else {
49-
console.error('Unexpected error:', error);
50-
}
51-
throw error;
52-
}
14+
type AmountType = 'EXACT_SRC_AMOUNT';
15+
type FeeTolerance = { type: 'PERCENT'; amount?: number };
16+
17+
type GetQuotesInput = {
18+
srcTokenAddress: string;
19+
dstTokenAddress: string;
20+
srcChainKey: string;
21+
dstChainKey: string;
22+
amount: string | bigint;
23+
srcWalletAddress: string;
24+
dstWalletAddress: string;
25+
options: {
26+
amountType?: AmountType;
27+
feeTolerance?: FeeTolerance;
28+
dstNativeDropAmount?: number | bigint;
29+
};
30+
};
31+
32+
type QuoteHead = { id: string };
33+
type GetQuotesResult = { quotes: QuoteHead[] };
34+
35+
type EvmEncodedTx = {
36+
chainId: number;
37+
to: `0x${string}`;
38+
data?: `0x${string}`;
39+
value?: string | bigint;
40+
from?: `0x${string}`;
41+
gasLimit?: string | bigint;
42+
};
43+
44+
type TransactionStep = {
45+
type: 'TRANSACTION';
46+
chainKey: string;
47+
chainType: 'EVM';
48+
description: string;
49+
signerAddress: `0x${string}`;
50+
transaction: { encoded: EvmEncodedTx };
51+
};
52+
53+
type SignatureStep = {
54+
type: 'SIGNATURE';
55+
description: string;
56+
chainKey?: string;
57+
signerAddress: `0x${string}`;
58+
signature: { type: 'EIP712'; typedData: TypedDataDefinition };
59+
};
60+
61+
type UserStep = TransactionStep | SignatureStep;
62+
63+
type BuildUserStepsResult = {
64+
body: { userSteps: UserStep[] };
65+
};
66+
67+
async function postJson<T>(path: string, body: unknown): Promise<T> {
68+
const res = await fetch(`${API}${path}`, {
69+
method: 'POST',
70+
headers: {
71+
'x-api-key': API_KEY,
72+
'Content-Type': 'application/json',
73+
},
74+
body: JSON.stringify(body),
75+
});
76+
if (!res.ok) throw new Error(await res.text());
77+
return res.json() as Promise<T>;
5378
}
5479

55-
async function executeStargateTransaction() {
56-
try {
57-
// 1. Fetch quotes data
58-
const routesData = await fetchStargateRoutes();
59-
60-
// 2. Get the first route (or implement your own selection logic)
61-
// Here you can select from all the supported routes including StargateV2:Taxi, StargateBus or CCTP
62-
// Supported routes are different for each token
63-
// Each route contains all transactions required to execute the transfer given in executable order
64-
const selectedRoute = routesData.quotes[0];
65-
if (!selectedRoute) {
66-
throw new Error('No quotes available');
67-
}
68-
69-
console.log('Selected route:', selectedRoute);
70-
71-
// Execute all transactions in the route steps
72-
for (let i = 0; i < selectedRoute.steps.length; i++) {
73-
const executableTransaction = selectedRoute.steps[i].transaction;
74-
console.log(`Executing step ${i + 1}/${selectedRoute.steps.length}:`, executableTransaction);
75-
76-
// Create transaction object, only include value if it exists and is not empty
77-
const txParams: Record<string, unknown> = {
78-
account,
79-
to: executableTransaction.to,
80-
data: executableTransaction.data,
81-
};
82-
83-
// Only add value if it exists and is not empty
84-
if (executableTransaction.value && executableTransaction.value !== '0') {
85-
txParams.value = BigInt(executableTransaction.value);
86-
}
87-
88-
// Execute the transaction
89-
const txHash = await walletClient.sendTransaction(txParams);
90-
console.log(`Step ${i + 1} transaction hash: ${txHash}`);
91-
92-
// Wait for transaction to be mined
93-
const receipt = await ethereumClient.waitForTransactionReceipt({ hash: txHash });
94-
console.log(`Step ${i + 1} transaction confirmed:`, receipt);
80+
async function fetchQuotes(): Promise<GetQuotesResult> {
81+
const payload: GetQuotesInput = {
82+
srcTokenAddress: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
83+
dstTokenAddress: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
84+
srcChainKey: 'optimism',
85+
dstChainKey: 'arbitrum',
86+
amount: '1000000000000000',
87+
srcWalletAddress: account.address,
88+
dstWalletAddress: account.address,
89+
options: {
90+
amountType: 'EXACT_SRC_AMOUNT',
91+
feeTolerance: { type: 'PERCENT', amount: 20 },
92+
dstNativeDropAmount: 0,
93+
},
94+
};
95+
return postJson<GetQuotesResult>('/quotes', payload);
96+
}
97+
98+
async function buildUserSteps(quoteId: string) {
99+
return postJson<BuildUserStepsResult>('/build-user-steps', { quoteId });
100+
}
101+
102+
async function submitSignature(quoteId: string, signatures: string[]) {
103+
await postJson<Record<string, never>>('/submit-signature', { quoteId, signatures });
104+
}
105+
106+
function toBigIntOrUndefined(v: string | bigint | undefined): bigint | undefined {
107+
if (v === undefined) return undefined;
108+
return typeof v === 'string' ? BigInt(v) : v;
109+
}
110+
111+
async function executeEvmTransaction(step: TransactionStep) {
112+
const tx = step.transaction.encoded;
113+
114+
const hash = await wallet.sendTransaction({
115+
account,
116+
to: tx.to,
117+
data: tx.data,
118+
value: toBigIntOrUndefined(tx.value) ?? 0n,
119+
});
120+
121+
await client.waitForTransactionReceipt({ hash });
122+
}
123+
124+
async function signEip712(step: SignatureStep) {
125+
const typed = step.signature.typedData;
126+
const signature = await wallet.signTypedData(typed);
127+
return signature;
128+
}
129+
130+
async function run() {
131+
const quotes = await fetchQuotes();
132+
const quote = quotes.quotes?.[0];
133+
if (!quote) throw new Error('No quote');
134+
135+
const { body } = await buildUserSteps(quote.id);
136+
for (const step of body.userSteps) {
137+
if (step.type === 'SIGNATURE') {
138+
const sig = await signEip712(step);
139+
await submitSignature(quote.id, [sig]);
140+
} else if (step.type === 'TRANSACTION') {
141+
await executeEvmTransaction(step);
95142
}
96-
97-
console.log('All steps executed successfully');
98-
return true;
99-
} catch (error) {
100-
console.error('Error executing Stargate transaction:', error);
101-
throw error;
102143
}
103144
}
104145

105-
// Execute the transaction
106-
void executeStargateTransaction()
107-
.then(() => {
108-
console.log('Successfully executed Stargate transaction');
109-
})
110-
.catch((err) => {
111-
console.error('Failed to execute Stargate transaction:', err);
112-
});
146+
void run();

0 commit comments

Comments
 (0)