118 |
119 | );
120 | }
121 |
122 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Avalanche Starter Kit
2 |
3 | This starter kit will get you started with developing solidity smart contract dApps on the C-Chain or on an Avalanche L1. It provides all tools to build cross-L1 dApps using Teleporter. It includes:
4 |
5 | - **Avalanche CLI**: Run a local Avalanche Network
6 | - **Foundry**:
7 | - Forge: Compile and Deploy smart contracts to the local network, Fuji Testnet or Mainnet
8 | - Cast: Interact with these smart contracts
9 | - **Teleporter**: All contracts you may want to interact with Teleporter
10 | - **AWM Relayer**: The binary to run your own relayer
11 | - **Examples**: Contracts showcasing how to achieve common patterns, such as sending simple messages, call functions of a contract on another blockchain and bridging assets. Please note that these example contracts have not been audited and are for educational purposes only
12 | - **BuilderKit**: A component library providing UI elements for ICTT bridges, Cross-Chain swaps, ...
13 |
14 | ## Set Up
15 |
16 | This starter kit utilizes a Dev Container specification. Dev Containers use containerization to create consistent and isolated development environments. All of the above mentioned components are pre-installed in that container. These containers can be run using GitHub Codespaces or locally using Docker and VS Code. You can switch back and forth between the two options.
17 |
18 | ### Run on Github Codespace
19 |
20 | You can run them directly on Github by clicking **Code**, switching to the **Codespaces** tab and clicking **Create codespace on main**. A new window will open that loads the codespace. Afterwards you will see a browser version of VS code with all the dependencies installed. Codespace time out after some time of inactivity, but can be restarted.
21 |
22 | ### Run Dev Container locally with Docker
23 |
24 | Alternatively, you can run them locally. You need [docker](https://www.docker.com/products/docker-desktop/) installed and [VS Code](https://code.visualstudio.com/) with the extensions [Dev Container extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers). Then clone the repository and open it in VS Code. VS Code will ask you if you want to reopen the project in a container.
25 |
26 | ## Starting a local Avalanche Network
27 |
28 | To start a local Avalanche network with your own teleporter-enabled L1 inside the container follow these commands. Your Avalanche network will be completely independent of the Avalanche Mainnet and Fuji Testnet. It will have its own Primary Network (C-Chain, X-Chain & P-Chain). You will not have access to services available on Fuji (such as Chainlink services or bridges). If you require these, go to the [Fuji Testnet](#fuji-testnet) section.
29 |
30 | First let's create out L1 configuration. Follow the dialog and if you don't have special requirements for precompiles just follow the suggested options. For the Airdrop of the native token select "Airdrop 1 million tokens to the default ewoq address (do not use in production)". Keep the name `myblockchain` to avoid additional configuration.
31 |
32 | ```
33 | avalanche blockchain create myblockchain
34 | ```
35 |
36 | Now let's spin up the local Avalanche network and deploy our L1. This will also deploy the Teleporter messenger and the registry on our L1 and the C-Chain.
37 |
38 | ```bash
39 | avalanche blockchain deploy myblockchain
40 | ```
41 |
42 | Make sure to add the RPC Url to the `foundry.toml` file if you have chosen a different name than `myblockchain`. If you've used `myblockchain` the rpc is already configured.
43 |
44 | ```toml
45 | [rpc_endpoints]
46 | local-c = "http://localhost:9650/ext/bc/C/rpc"
47 | myblockchain = "http://localhost:9650/ext/bc/myblockchain/rpc"
48 | anotherblockchain = "http://localhost:9650/ext/bc/BASE58_BLOCKCHAIN_ID/rpc"
49 | ```
50 |
51 | ## Code Examples
52 |
53 | ### Interchain Messaging
54 | - [send-receive](https://academy.avax.network/course/interchain-messaging/04-icm-basics/01-icm-basics)
55 | - [send-roundtrip](https://academy.avax.network/course/interchain-messaging/05-two-way-communication/01-two-way-communication)
56 | - [invoking-functions](https://academy.avax.network/course/interchain-messaging/06-invoking-functions/01-invoking-functions)
57 | - [registry](https://academy.avax.network/course/interchain-messaging/07-icm-registry/01-icm-registry)
58 | - [incentivized-relayer](https://academy.avax.network/course/interchain-messaging/12-incentivizing-a-relayer/01-incentivizing-a-relayer)
59 |
60 | ### Interchain Token Transfer
61 | - [erc20-to-erc20](https://academy.avax.network/course/interchain-token-transfer/06-erc-20-to-erc-20-bridge/01-erc-20-to-erc-20-bridge)
62 | - [native-to-erc20](https://academy.avax.network/course/interchain-token-transfer/08-native-to-erc-20-bridge/01-native-to-erc-20-bridge)
63 | - [native-to-native](https://academy.avax.network/course/l1-tokenomics/03-multi-chain-ecosystems/04-use-any-native-as-native-token)
64 | - [erc20-to-native](https://academy.avax.network/course/l1-tokenomics/03-multi-chain-ecosystems/03-use-erc20-as-native-token)
65 | - [cross-chain-token-swaps](https://academy.avax.network/course/interchain-token-transfer/13-cross-chain-token-swaps/07-exchange-contract)
66 |
67 | ### Misc
68 | - [creating-contracts](contracts/misc/creating-contracts/REAEDME.md)
69 | - [erc721-bridge](contracts/misc/erc721-bridge/README.md)
70 |
71 |
72 | ## Web-Apps
73 | - [AvaCloud APIs](https://academy.avax.network/course/avacloudapis)
74 |
--------------------------------------------------------------------------------
/contracts/interchain-token-transfer/cross-chain-token-swaps/DexERC20Wrapper.sol:
--------------------------------------------------------------------------------
1 | // (c) 2024, Ava Labs, Inc. All rights reserved.
2 | // See the file LICENSE for licensing terms.
3 |
4 | // SPDX-License-Identifier: Ecosystem
5 |
6 | pragma solidity ^0.8.25;
7 |
8 | import {IERC20SendAndCallReceiver} from "@avalanche-interchain-token-transfer/interfaces/IERC20SendAndCallReceiver.sol";
9 | import {SafeERC20TransferFrom} from "@avalanche-interchain-token-transfer/../utilities/SafeERC20TransferFrom.sol";
10 |
11 | import {SafeERC20} from "@openzeppelin/contracts@5.0.2/token/ERC20/utils/SafeERC20.sol";
12 | import {IERC20} from "@openzeppelin/contracts@5.0.2/token/ERC20/IERC20.sol";
13 | import {Context} from "@openzeppelin/contracts@5.0.2/utils/Context.sol";
14 |
15 | import {IWAVAX} from "./interfaces/IWAVAX.sol";
16 | import {IUniswapFactory} from "./interfaces/IUniswapFactory.sol";
17 | import {IUniswapPair} from "./interfaces/IUniswapPair.sol";
18 |
19 | /**
20 | * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
21 | * DO NOT USE THIS CODE IN PRODUCTION.
22 | */
23 | contract DexERC20Wrapper is Context, IERC20SendAndCallReceiver {
24 | using SafeERC20 for IERC20;
25 |
26 | address public immutable WNATIVE;
27 | address public immutable factory;
28 |
29 | struct SwapOptions {
30 | address tokenOut;
31 | uint256 minAmountOut;
32 | }
33 |
34 | constructor(address wrappedNativeAddress, address dexFactoryAddress) {
35 | WNATIVE = wrappedNativeAddress;
36 | factory = dexFactoryAddress;
37 | }
38 |
39 | event TokensReceived(
40 | bytes32 indexed sourceBlockchainID,
41 | address indexed originTokenTransferrerAddress,
42 | address indexed originSenderAddress,
43 | address token,
44 | uint256 amount,
45 | bytes payload
46 | );
47 |
48 | // To receive native when another contract called.
49 | receive() external payable {}
50 |
51 | function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut)
52 | internal
53 | pure
54 | returns (uint256 amountOut)
55 | {
56 | uint256 amountInWithFee = amountIn * 997;
57 | uint256 numerator = amountInWithFee * reserveOut;
58 | uint256 denominator = reserveIn * 1e3 + amountInWithFee;
59 | amountOut = numerator / denominator;
60 | }
61 |
62 | function query(uint256 amountIn, address tokenIn, address tokenOut) internal view returns (uint256 amountOut) {
63 | if (tokenIn == tokenOut || amountIn == 0) {
64 | return 0;
65 | }
66 | address pair = IUniswapFactory(factory).getPair(tokenIn, tokenOut);
67 | if (pair == address(0)) {
68 | return 0;
69 | }
70 | (uint256 r0, uint256 r1,) = IUniswapPair(pair).getReserves();
71 | (uint256 reserveIn, uint256 reserveOut) = tokenIn < tokenOut ? (r0, r1) : (r1, r0);
72 | if (reserveIn > 0 && reserveOut > 0) {
73 | amountOut = getAmountOut(amountIn, reserveIn, reserveOut);
74 | }
75 | }
76 |
77 | function swap(uint256 amountIn, uint256 amountOut, address tokenIn, address tokenOut, address to) internal {
78 | address pair = IUniswapFactory(factory).getPair(tokenIn, tokenOut);
79 | (uint256 amount0Out, uint256 amount1Out) =
80 | (tokenIn < tokenOut) ? (uint256(0), amountOut) : (amountOut, uint256(0));
81 | IERC20(tokenIn).safeTransfer(pair, amountIn);
82 | IUniswapPair(pair).swap(amount0Out, amount1Out, to, new bytes(0));
83 | }
84 |
85 | function receiveTokens(
86 | bytes32 sourceBlockchainID,
87 | address originTokenTransferrerAddress,
88 | address originSenderAddress,
89 | address token,
90 | uint256 amount,
91 | bytes calldata payload
92 | ) external {
93 | emit TokensReceived({
94 | sourceBlockchainID: sourceBlockchainID,
95 | originTokenTransferrerAddress: originTokenTransferrerAddress,
96 | originSenderAddress: originSenderAddress,
97 | token: token,
98 | amount: amount,
99 | payload: payload
100 | });
101 |
102 | require(payload.length > 0, "DexERC20Wrapper: empty payload");
103 |
104 | IERC20 _token = IERC20(token);
105 | // Receives teleported assets to be used for different purposes.
106 | SafeERC20TransferFrom.safeTransferFrom(_token, _msgSender(), amount);
107 |
108 | // Requests a quote from the Uniswap V2-like contract.
109 | uint256 amountOut = query(amount, token, WNATIVE);
110 | require(amountOut > 0, "DexERC20Wrapper: insufficient liquidity");
111 |
112 | // Parses the payload of the message.
113 | SwapOptions memory swapOptions = abi.decode(payload, (SwapOptions));
114 | // Checks if the target swap price is still valid.
115 | require(amountOut >= swapOptions.minAmountOut, "DexERC20Wrapper: slippage exceeded");
116 |
117 | // Verifies if the desired tokenOut is a native or wrapped asset.
118 | if (swapOptions.tokenOut == address(0)) {
119 | swap(amount, amountOut, token, WNATIVE, address(this));
120 | IWAVAX(WNATIVE).withdraw(amountOut);
121 | payable(originSenderAddress).transfer(amountOut);
122 | } else {
123 | swap(amount, amountOut, token, WNATIVE, originSenderAddress);
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/web-apps/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 |
3 | export default function Home() {
4 | return (
5 |
6 |