Relayer and UI Cleanup (#3)
* contracts: fix ICircleIntegration interface ui: fix addresses and number of confirms offchain-relayer: refactor * offchain-relayer: fix package.json Co-authored-by: A5 Pickle <a5-pickle@users.noreply.github.com>
This commit is contained in:
parent
b987f739b6
commit
34cd2c5887
|
@ -1,6 +1,6 @@
|
||||||
## NativeSwap
|
## NativeSwap
|
||||||
|
|
||||||
https://certusone.github.io/nativeswap-usdc-example/
|
https://wormhole-foundation.github.io/example-nativeswap-usdc/
|
||||||
|
|
||||||
This is a non-production example program.
|
This is a non-production example program.
|
||||||
|
|
||||||
|
|
|
@ -29,11 +29,10 @@ interface ICircleIntegration {
|
||||||
bytes payload;
|
bytes payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
function transferTokensWithPayload(
|
function transferTokensWithPayload(TransferParameters memory transferParams, uint32 batchId, bytes memory payload)
|
||||||
TransferParameters memory transferParams,
|
external
|
||||||
uint32 batchId,
|
payable
|
||||||
bytes memory payload
|
returns (uint64 messageSequence);
|
||||||
) external payable returns (uint64 messageSequence);
|
|
||||||
|
|
||||||
function redeemTokensWithPayload(RedeemParameters memory params)
|
function redeemTokensWithPayload(RedeemParameters memory params)
|
||||||
external
|
external
|
||||||
|
@ -44,4 +43,6 @@ interface ICircleIntegration {
|
||||||
function getDomainFromChainId(uint16 chainId_) external view returns (uint32);
|
function getDomainFromChainId(uint16 chainId_) external view returns (uint32);
|
||||||
|
|
||||||
function getChainIdFromDomain(uint32 domain) external view returns (uint16);
|
function getChainIdFromDomain(uint32 domain) external view returns (uint16);
|
||||||
|
|
||||||
|
function isMessageConsumed(bytes32 hash) external view returns (bool);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
"typescript": "^4.8.4"
|
"typescript": "^4.8.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "typechain --target=ethers-v5 --out-dir=src/ethers-contracts ../contracts/build/contracts/*.json",
|
"build-types": "typechain --target=ethers-v5 --out-dir=src/ethers-contracts ../contracts/build/contracts/*.json",
|
||||||
|
"build": "npm run build-types",
|
||||||
"clean": "rm -rf node_modules src/ethers-contracts",
|
"clean": "rm -rf node_modules src/ethers-contracts",
|
||||||
"start": "npm run build && ts-node src/main.ts"
|
"start": "npm run build && ts-node src/main.ts"
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,9 @@ import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport"
|
||||||
import {
|
import {
|
||||||
CrossChainSwapV2__factory,
|
CrossChainSwapV2__factory,
|
||||||
CrossChainSwapV3__factory,
|
CrossChainSwapV3__factory,
|
||||||
IUSDCIntegration__factory,
|
ICircleIntegration__factory,
|
||||||
} from "./ethers-contracts";
|
} from "./ethers-contracts";
|
||||||
|
import { WebSocketProvider } from "./websocket";
|
||||||
|
|
||||||
const WORMHOLE_RPC_HOSTS = ["https://wormhole-v2-testnet-api.certus.one"];
|
const WORMHOLE_RPC_HOSTS = ["https://wormhole-v2-testnet-api.certus.one"];
|
||||||
|
|
||||||
|
@ -22,12 +23,22 @@ const WORMHOLE_RPC_HOSTS = ["https://wormhole-v2-testnet-api.certus.one"];
|
||||||
const receiptMaxAttempts = Number(process.env.RECEIPT_MAX_ATTEMPTS!);
|
const receiptMaxAttempts = Number(process.env.RECEIPT_MAX_ATTEMPTS!);
|
||||||
const receiptTimeout = Number(process.env.RECEIPT_TIMEOUT!);
|
const receiptTimeout = Number(process.env.RECEIPT_TIMEOUT!);
|
||||||
|
|
||||||
|
const attestationTimeout = Number(process.env.ATTESTATION_TIMEOUT!);
|
||||||
|
console.log(`attestation timeout: ${attestationTimeout}`);
|
||||||
|
|
||||||
|
const attestationMaxAttempts = Number(process.env.ATTESTATION_MAX_ATTEMPTS!);
|
||||||
|
console.log(`attestation max attempts: ${attestationMaxAttempts}`);
|
||||||
|
|
||||||
|
const vaaTimeout = Number(process.env.ATTESTATION_TIMEOUT!);
|
||||||
|
console.log(`vaa timeout: ${vaaTimeout}`);
|
||||||
|
|
||||||
|
const vaaMaxAttempts = Number(process.env.ATTESTATION_MAX_ATTEMPTS!);
|
||||||
|
console.log(`vaa max attempts: ${vaaMaxAttempts}`);
|
||||||
|
|
||||||
const srcContractType = process.env.SRC_CONTRACT_TYPE!;
|
const srcContractType = process.env.SRC_CONTRACT_TYPE!;
|
||||||
const circleEmitter = process.env.CIRCLE_EMITTER!;
|
const circleEmitter = process.env.CIRCLE_EMITTER!;
|
||||||
|
|
||||||
const srcProvider = new ethers.providers.WebSocketProvider(
|
const srcProvider = new WebSocketProvider(process.env.SRC_RPC!);
|
||||||
process.env.SRC_RPC!
|
|
||||||
);
|
|
||||||
const dstWallet = new ethers.Wallet(
|
const dstWallet = new ethers.Wallet(
|
||||||
process.env.PRIVATE_KEY!,
|
process.env.PRIVATE_KEY!,
|
||||||
new ethers.providers.StaticJsonRpcProvider(process.env.DST_RPC!)
|
new ethers.providers.StaticJsonRpcProvider(process.env.DST_RPC!)
|
||||||
|
@ -56,18 +67,14 @@ const WORMHOLE_RPC_HOSTS = ["https://wormhole-v2-testnet-api.certus.one"];
|
||||||
const srcChainId = await wormhole.chainId().then((id) => id as ChainId);
|
const srcChainId = await wormhole.chainId().then((id) => id as ChainId);
|
||||||
|
|
||||||
const wormCircle = await srcContract
|
const wormCircle = await srcContract
|
||||||
.USDC_INTEGRATION()
|
.CIRCLE_INTEGRATION()
|
||||||
.then((address) => IUSDCIntegration__factory.connect(address, srcProvider));
|
.then((address) =>
|
||||||
|
ICircleIntegration__factory.connect(address, srcProvider)
|
||||||
|
);
|
||||||
|
|
||||||
// let's go
|
// let's go
|
||||||
console.log(
|
console.log("starting relayer");
|
||||||
"starting relayer",
|
console.log(`src: wormhole chain id: ${srcChainId}`);
|
||||||
srcProvider.network.chainId,
|
|
||||||
"to",
|
|
||||||
await dstWallet.getChainId(),
|
|
||||||
"chainId",
|
|
||||||
srcChainId
|
|
||||||
);
|
|
||||||
|
|
||||||
// collect pending transactions
|
// collect pending transactions
|
||||||
const pendingTxHashes: string[] = [];
|
const pendingTxHashes: string[] = [];
|
||||||
|
@ -81,7 +88,7 @@ const WORMHOLE_RPC_HOSTS = ["https://wormhole-v2-testnet-api.certus.one"];
|
||||||
|
|
||||||
// process transactions here
|
// process transactions here
|
||||||
while (true) {
|
while (true) {
|
||||||
while (pendingTxHashes.length > 0) {
|
if (pendingTxHashes.length > 0) {
|
||||||
// get first transaction hash
|
// get first transaction hash
|
||||||
const txHash = pendingTxHashes[0];
|
const txHash = pendingTxHashes[0];
|
||||||
|
|
||||||
|
@ -99,70 +106,90 @@ const WORMHOLE_RPC_HOSTS = ["https://wormhole-v2-testnet-api.certus.one"];
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we really have a receipt, process the logs
|
// if we really have a receipt, process the logs
|
||||||
if (receipt !== null && receipt.to == srcContract.address) {
|
if (receipt === null || receipt.to != srcContract.address) {
|
||||||
console.log("found transaction", receipt.transactionHash);
|
pendingTxHashes.shift();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// find circle message
|
console.log(`found transaction ${txHash}`);
|
||||||
const [circleBridgeMessage, circleAttestation] =
|
|
||||||
await handleCircleMessageInLogs(receipt.logs, circleEmitter);
|
|
||||||
if (circleBridgeMessage === null || circleAttestation === null) {
|
|
||||||
console.log("we have a problem... ignore?", receipt);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetch wormhole message sequence
|
// fetch wormhole message sequence
|
||||||
const sequences = parseSequencesFromLogEth(receipt, wormhole.address);
|
const sequences = parseSequencesFromLogEth(receipt, wormhole.address);
|
||||||
if (sequences.length == 0) {
|
if (sequences.length == 0) {
|
||||||
console.log("Cannot find any wormhole sequences?", receipt);
|
console.log(`probably just a redeem. moving on`);
|
||||||
continue;
|
pendingTxHashes.shift();
|
||||||
}
|
continue;
|
||||||
const sequence = sequences[0];
|
}
|
||||||
|
const sequence = sequences[0];
|
||||||
// now fetched the signed message
|
|
||||||
const { vaaBytes: encodedWormholeMessage } =
|
|
||||||
await getSignedVAAWithRetry(
|
|
||||||
WORMHOLE_RPC_HOSTS,
|
|
||||||
srcChainId,
|
|
||||||
getEmitterAddressEth(wormCircle.address),
|
|
||||||
sequence,
|
|
||||||
{
|
|
||||||
transport: NodeHttpTransport(),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
// now fetched the signed message
|
||||||
|
const result = await getSignedVAAWithRetry(
|
||||||
|
WORMHOLE_RPC_HOSTS,
|
||||||
|
srcChainId,
|
||||||
|
getEmitterAddressEth(wormCircle.address),
|
||||||
|
sequence,
|
||||||
{
|
{
|
||||||
const receipt = await dstContract
|
transport: NodeHttpTransport(),
|
||||||
.recvAndSwapExactNativeIn({
|
},
|
||||||
encodedWormholeMessage,
|
vaaTimeout,
|
||||||
circleBridgeMessage,
|
vaaMaxAttempts
|
||||||
circleAttestation,
|
).catch((reason) => null);
|
||||||
})
|
|
||||||
.catch((reason) => {
|
|
||||||
console.log(reason);
|
|
||||||
return null;
|
|
||||||
})
|
|
||||||
.then((tx) => {
|
|
||||||
if (tx === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tx.wait() as Promise<ethers.ContractReceipt>;
|
if (result === null) {
|
||||||
});
|
console.log(`cannot find signed vaa for ${txHash}`);
|
||||||
if (receipt !== null) {
|
pendingTxHashes.shift();
|
||||||
console.log("relayed", receipt.transactionHash);
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { vaaBytes: encodedWormholeMessage } = result;
|
||||||
|
|
||||||
|
// find circle message
|
||||||
|
const [circleBridgeMessage, circleAttestation] =
|
||||||
|
await handleCircleMessageInLogs(
|
||||||
|
receipt.logs,
|
||||||
|
circleEmitter,
|
||||||
|
attestationTimeout,
|
||||||
|
attestationMaxAttempts
|
||||||
|
);
|
||||||
|
if (circleBridgeMessage === null || circleAttestation === null) {
|
||||||
|
console.log(`cannot attest Circle message for ${txHash}`);
|
||||||
|
pendingTxHashes.shift();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const redeemReceipt = await dstContract
|
||||||
|
.recvAndSwapExactNativeIn({
|
||||||
|
encodedWormholeMessage,
|
||||||
|
circleBridgeMessage,
|
||||||
|
circleAttestation,
|
||||||
|
})
|
||||||
|
.catch((reason) => {
|
||||||
|
console.log(reason);
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.then((tx) => {
|
||||||
|
if (tx === null) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return tx.wait() as Promise<ethers.ContractReceipt>;
|
||||||
|
});
|
||||||
|
if (redeemReceipt !== null) {
|
||||||
|
console.log(`relayed ${redeemReceipt.transactionHash}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
pendingTxHashes.shift();
|
pendingTxHashes.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
await sleep(relayerTimeout);
|
await sleep(relayerTimeout);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
async function handleCircleMessageInLogs(
|
async function handleCircleMessageInLogs(
|
||||||
logs: ethers.providers.Log[],
|
logs: ethers.providers.Log[],
|
||||||
circleEmitterAddress: string
|
circleEmitterAddress: string,
|
||||||
|
attestationTimeout: number,
|
||||||
|
attestationMaxAttempts: number
|
||||||
): Promise<[string | null, string | null]> {
|
): Promise<[string | null, string | null]> {
|
||||||
const circleMessage = await findCircleMessageInLogs(
|
const circleMessage = await findCircleMessageInLogs(
|
||||||
logs,
|
logs,
|
||||||
|
@ -173,7 +200,14 @@ async function handleCircleMessageInLogs(
|
||||||
}
|
}
|
||||||
|
|
||||||
const circleMessageHash = ethers.utils.keccak256(circleMessage);
|
const circleMessageHash = ethers.utils.keccak256(circleMessage);
|
||||||
const signature = await getCircleAttestation(circleMessageHash);
|
const signature = await getCircleAttestation(
|
||||||
|
circleMessageHash,
|
||||||
|
attestationTimeout,
|
||||||
|
attestationMaxAttempts
|
||||||
|
);
|
||||||
|
if (signature === null) {
|
||||||
|
return [null, null];
|
||||||
|
}
|
||||||
|
|
||||||
return [circleMessage, signature];
|
return [circleMessage, signature];
|
||||||
}
|
}
|
||||||
|
@ -200,9 +234,11 @@ async function sleep(timeout: number) {
|
||||||
|
|
||||||
async function getCircleAttestation(
|
async function getCircleAttestation(
|
||||||
messageHash: ethers.BytesLike,
|
messageHash: ethers.BytesLike,
|
||||||
timeout: number = 2000
|
timeout: number,
|
||||||
|
maxAttempts: number
|
||||||
) {
|
) {
|
||||||
while (true) {
|
//while (true) {
|
||||||
|
for (let i = 0; i < maxAttempts; ++i) {
|
||||||
// get the post
|
// get the post
|
||||||
const response = await axios
|
const response = await axios
|
||||||
.get(`https://iris-api-sandbox.circle.com/attestations/${messageHash}`)
|
.get(`https://iris-api-sandbox.circle.com/attestations/${messageHash}`)
|
||||||
|
@ -227,4 +263,6 @@ async function getCircleAttestation(
|
||||||
|
|
||||||
await sleep(timeout);
|
await sleep(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
|
||||||
|
const WEBSOCKET_PING_INTERVAL = 10000;
|
||||||
|
const WEBSOCKET_PONG_TIMEOUT = 5000;
|
||||||
|
const WEBSOCKET_RECONNECT_DELAY = 100;
|
||||||
|
|
||||||
|
const WebSocketProviderClass =
|
||||||
|
(): new () => ethers.providers.WebSocketProvider => class {} as never;
|
||||||
|
|
||||||
|
export class WebSocketProvider extends WebSocketProviderClass() {
|
||||||
|
private provider?: ethers.providers.WebSocketProvider;
|
||||||
|
private events: ethers.providers.WebSocketProvider["_events"] = [];
|
||||||
|
private requests: ethers.providers.WebSocketProvider["_requests"] = {};
|
||||||
|
|
||||||
|
private handler = {
|
||||||
|
get(target: WebSocketProvider, prop: string, receiver: unknown) {
|
||||||
|
const value =
|
||||||
|
target.provider && Reflect.get(target.provider, prop, receiver);
|
||||||
|
|
||||||
|
return value instanceof Function ? value.bind(target.provider) : value;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(private providerUrl: any) {
|
||||||
|
super();
|
||||||
|
this.create();
|
||||||
|
|
||||||
|
return new Proxy(this, this.handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
private create() {
|
||||||
|
if (this.provider) {
|
||||||
|
this.events = [...this.events, ...this.provider._events];
|
||||||
|
this.requests = { ...this.requests, ...this.provider._requests };
|
||||||
|
}
|
||||||
|
|
||||||
|
const provider = new ethers.providers.WebSocketProvider(
|
||||||
|
this.providerUrl,
|
||||||
|
this.provider?.network?.chainId
|
||||||
|
);
|
||||||
|
let pingInterval: NodeJS.Timer | undefined;
|
||||||
|
let pongTimeout: NodeJS.Timeout | undefined;
|
||||||
|
|
||||||
|
provider._websocket.on("open", () => {
|
||||||
|
pingInterval = setInterval(() => {
|
||||||
|
provider._websocket.ping();
|
||||||
|
|
||||||
|
pongTimeout = setTimeout(() => {
|
||||||
|
provider._websocket.terminate();
|
||||||
|
}, WEBSOCKET_PONG_TIMEOUT);
|
||||||
|
}, WEBSOCKET_PING_INTERVAL);
|
||||||
|
|
||||||
|
let event;
|
||||||
|
while ((event = this.events.pop())) {
|
||||||
|
provider._events.push(event);
|
||||||
|
provider._startEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key in this.requests) {
|
||||||
|
provider._requests[key] = this.requests[key];
|
||||||
|
provider._websocket.send(this.requests[key].payload);
|
||||||
|
delete this.requests[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
provider._websocket.on("pong", () => {
|
||||||
|
if (pongTimeout) clearTimeout(pongTimeout);
|
||||||
|
});
|
||||||
|
|
||||||
|
provider._websocket.on("close", (code: number) => {
|
||||||
|
provider._wsReady = false;
|
||||||
|
|
||||||
|
if (pingInterval) clearInterval(pingInterval);
|
||||||
|
if (pongTimeout) clearTimeout(pongTimeout);
|
||||||
|
|
||||||
|
if (code !== 1000) {
|
||||||
|
setTimeout(() => this.create(), WEBSOCKET_RECONNECT_DELAY);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.provider = provider;
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,7 +60,7 @@ export default function TransactionProgress({
|
||||||
? currentBlock - txBlockNumber
|
? currentBlock - txBlockNumber
|
||||||
: 0;
|
: 0;
|
||||||
const expectedBlocks =
|
const expectedBlocks =
|
||||||
chainId === CHAIN_ID_POLYGON ? 512 : CHAIN_ID_AVAX ? 1 : 15;
|
chainId === CHAIN_ID_POLYGON ? 512 : 1; //CHAIN_ID_AVAX ? 1 : 15;
|
||||||
blockDiff = Math.min(Math.max(blockDiff, 0), expectedBlocks);
|
blockDiff = Math.min(Math.max(blockDiff, 0), expectedBlocks);
|
||||||
let value;
|
let value;
|
||||||
let valueBuffer;
|
let valueBuffer;
|
||||||
|
|
|
@ -20,8 +20,8 @@ import {
|
||||||
UniswapToUniswapQuoter,
|
UniswapToUniswapQuoter,
|
||||||
} from "../route/cross-quote";
|
} from "../route/cross-quote";
|
||||||
import {
|
import {
|
||||||
CIRCLE_EMITTER_ADDRESS_ETHEREUM,
|
CIRCLE_INTEGRATION_ADDRESS_ETHEREUM,
|
||||||
CIRCLE_EMITTER_ADDRESS_AVALANCHE,
|
CIRCLE_INTEGRATION_ADDRESS_AVALANCHE,
|
||||||
CORE_BRIDGE_ADDRESS_ETHEREUM,
|
CORE_BRIDGE_ADDRESS_ETHEREUM,
|
||||||
CORE_BRIDGE_ADDRESS_AVALANCHE,
|
CORE_BRIDGE_ADDRESS_AVALANCHE,
|
||||||
WORMHOLE_RPC_HOSTS,
|
WORMHOLE_RPC_HOSTS,
|
||||||
|
@ -74,7 +74,7 @@ const EXECUTION_PARAMETERS_ETHEREUM: ExecutionParameters = {
|
||||||
wormhole: {
|
wormhole: {
|
||||||
chainId: CHAIN_ID_ETH,
|
chainId: CHAIN_ID_ETH,
|
||||||
coreBridgeAddress: CORE_BRIDGE_ADDRESS_ETHEREUM,
|
coreBridgeAddress: CORE_BRIDGE_ADDRESS_ETHEREUM,
|
||||||
circleEmitterAddress: CIRCLE_EMITTER_ADDRESS_ETHEREUM,
|
circleEmitterAddress: CIRCLE_INTEGRATION_ADDRESS_ETHEREUM,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ const EXECUTION_PARAMETERS_AVALANCHE: ExecutionParameters = {
|
||||||
wormhole: {
|
wormhole: {
|
||||||
chainId: CHAIN_ID_AVAX,
|
chainId: CHAIN_ID_AVAX,
|
||||||
coreBridgeAddress: CORE_BRIDGE_ADDRESS_AVALANCHE,
|
coreBridgeAddress: CORE_BRIDGE_ADDRESS_AVALANCHE,
|
||||||
circleEmitterAddress: CIRCLE_EMITTER_ADDRESS_AVALANCHE,
|
circleEmitterAddress: CIRCLE_INTEGRATION_ADDRESS_AVALANCHE,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -86,11 +86,11 @@ export const CORE_BRIDGE_ADDRESS_AVALANCHE =
|
||||||
"0x7bbcE28e64B3F8b84d876Ab298393c38ad7aac4C";
|
"0x7bbcE28e64B3F8b84d876Ab298393c38ad7aac4C";
|
||||||
|
|
||||||
// circle integration
|
// circle integration
|
||||||
export const CIRCLE_EMITTER_ADDRESS_ETHEREUM =
|
export const CIRCLE_INTEGRATION_ADDRESS_ETHEREUM =
|
||||||
"0xdbedb4ebd098e9f1777af9f8088e794d381309d1";
|
"0xbdCc4eBE3157df347671e078a41eE5Ce137Cd306";
|
||||||
|
|
||||||
export const CIRCLE_EMITTER_ADDRESS_AVALANCHE =
|
export const CIRCLE_INTEGRATION_ADDRESS_AVALANCHE =
|
||||||
"0x3e6a4543165aaecbf7ffc81e54a1c7939cb12cb8";
|
"0xb200977d46aea35ce6368d181534f413570a0f54";
|
||||||
|
|
||||||
// gas
|
// gas
|
||||||
export const APPROVAL_GAS_LIMIT = "100000";
|
export const APPROVAL_GAS_LIMIT = "100000";
|
||||||
|
|
|
@ -43,7 +43,7 @@ import SwapProgress from "../components/SwapProgress";
|
||||||
import Footer from "../components/Footer";
|
import Footer from "../components/Footer";
|
||||||
import TerraWalletKey from "../components/TerraWalletKey";
|
import TerraWalletKey from "../components/TerraWalletKey";
|
||||||
import useIsWalletReady from "../hooks/useIsWalletReady";
|
import useIsWalletReady from "../hooks/useIsWalletReady";
|
||||||
import { IWormhole__factory, IUSDCIntegration__factory } from "../ethers-contracts";
|
import { IWormhole__factory, ICircleIntegration__factory } from "../ethers-contracts";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
bg: {
|
bg: {
|
||||||
|
@ -408,7 +408,7 @@ export default function Home() {
|
||||||
provider
|
provider
|
||||||
).parseVM(vaaBytes);
|
).parseVM(vaaBytes);
|
||||||
|
|
||||||
const circleEmitter = IUSDCIntegration__factory.connect(
|
const circleEmitter = ICircleIntegration__factory.connect(
|
||||||
executor.dstExecutionParams.wormhole.circleEmitterAddress,
|
executor.dstExecutionParams.wormhole.circleEmitterAddress,
|
||||||
executor.quoter.getDstEvmProvider()!,
|
executor.quoter.getDstEvmProvider()!,
|
||||||
)
|
)
|
||||||
|
@ -605,15 +605,7 @@ export default function Home() {
|
||||||
Goerli Faucet
|
Goerli Faucet
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="https://faucet.polygon.technology/"
|
href="https://faucet.avax.network/"
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
style={{ margin: "5px" }}
|
|
||||||
>
|
|
||||||
Mumbai Faucet
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="https://faucet.avax-test.network/"
|
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
style={{ margin: "5px" }}
|
style={{ margin: "5px" }}
|
||||||
|
@ -621,15 +613,7 @@ export default function Home() {
|
||||||
Fuji Faucet
|
Fuji Faucet
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="https://testnet.binance.org/faucet-smart/"
|
href="https://github.com/wormhole-foundation/example-nativeswap-usdc/"
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
style={{ margin: "5px" }}
|
|
||||||
>
|
|
||||||
BSC Faucet
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="https://github.com/certusone/nativeswap-usdc-example/"
|
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
style={{ margin: "5px" }}
|
style={{ margin: "5px" }}
|
||||||
|
|
Loading…
Reference in New Issue