Swap and pools
The testnet DEX is Uniswap v2 (core and periphery, GPL-3.0, unmodified
math) on Aeva’s EVM. Its pools hold AE-20 tokens, the ERC-20 view of native
Aeva rights, so a swap moves native rights: the explorer shows an
EventTransferred from the pool’s address, and the policy of the asset
applies as it does to any transfer. Testnet, tokens have no value.
Run the setup block first; this guide also uses
cast.
The contracts
| Contract | Address |
|---|---|
Router (UniswapV2Router02) | 0xf2ea4660567aaA99e7061B5c88C7Fa35Bf5FEA39 |
| Factory | 0x73959bdf411C9f73eFA08e05a7073418A3CD33F7 |
| WAEVA (wrapped AEVA) | 0x53eD54339c3dFbEf259F431071e09e4831d13FDE |
| aeUSD (6 decimals) | 0x5aB37c022D0ce153F1C1949a88a93A8DC3E546D7 |
Each pool pairs aeUSD with a test stock’s bundle token (all its kinds as one ERC-20) or with WAEVA:
| Pool | Pair | Other token |
|---|---|---|
| AEVA / aeUSD | 0xFC404f637fBa0a964D3Dee6DAb417B3DD8c8A3A3 | WAEVA |
| tTSLA / aeUSD | 0x6ACB18102bCF1a889673B267CdE278d324C61fD6 | 0xA1A7C28D97A74E803E7f1237cE49f8272669c22a |
| tNVDA / aeUSD | 0xCA3c77AC85c374b34C57FDf0b803dF6dE02284A3 | 0xe6239aA4136Eb34149d8d3b47b2fC73BAbeca4fE |
| tAAPL / aeUSD | 0xF637A4b041cF827e98f5f5E15813023A9AE33904 | 0x3AcfD0e4c178474d452B61B5e0C57321def4E998 |
| tAMZN / aeUSD | 0x1F53c6Ff0ec596C2864CDDcdB853aAb4180e7a51 | 0x55b22C594f43E68029575d0d088A16c34cAc992F |
| tPLTR / aeUSD | 0xf7ACFE70E80AC7885D38A0723c3dBdEEA3A5aEaa | 0x66D45B79305B45Bf0d9BF2E884BD844791faD974 |
The pair init code hash is
147ac9b133c17a106ca8749a1705605798b9173af80c790796e2716b3f24cafd; the build is
reproducible from dex/ in the repository.
1. Read a pool and quote a swap
ROUTER=0xf2ea4660567aaA99e7061B5c88C7Fa35Bf5FEA39
AEUSD=0x5aB37c022D0ce153F1C1949a88a93A8DC3E546D7
TAAPL=0x3AcfD0e4c178474d452B61B5e0C57321def4E998
PAIR=0xF637A4b041cF827e98f5f5E15813023A9AE33904
cast call "$PAIR" "getReserves()(uint112,uint112,uint32)" --rpc-url "$AEVA_EVM"
cast call "$ROUTER" "getAmountsOut(uint256,address[])(uint256[])" 10000000 "[$AEUSD,$TAAPL]" --rpc-url "$AEVA_EVM"<reserve0> [<…>]
<reserve1> [<…>]
<timestamp>
[10000000 [1e7], <units out> [<…>]]A pair orders its tokens by address: tAAPL (0x3Acf…) sorts before aeUSD
(0x5aB3…), so reserve0 is tAAPL and reserve1 aeUSD, at the time of
writing 2 000 shares against 400 000 aeUSD, 200 aeUSD a share. The quote is the
tAAPL units (six decimals) that 10 aeUSD buys after the 0.3 % pool fee, about
0.05 of a share.
2. Swap 10 aeUSD for tAAPL
Your Aeva key is also an Ethereum key: the same secp256k1 key signs both, and
0x… and aeva1… are two spellings of one account.
unsafe-export-eth-key prints the private key. Do this with the throwaway
test keyring only.
PK=$(aevad keys unsafe-export-eth-key me --home "$AEVA_HOME" --keyring-backend test)
ME_HEX=$(cast wallet address --private-key "$PK")
OUT=$(cast call "$ROUTER" "getAmountsOut(uint256,address[])(uint256[])" 10000000 "[$AEUSD,$TAAPL]" --rpc-url "$AEVA_EVM" | tr -d '[]' | awk -F', ' '{print $2}' | awk '{print $1}')
MIN=$((OUT * 99 / 100))
cast send "$AEUSD" "approve(address,uint256)" "$ROUTER" 10000000 --private-key "$PK" --rpc-url "$AEVA_EVM" >/dev/null
cast send "$ROUTER" "swapExactTokensForTokens(uint256,uint256,address[],address,uint256)" 10000000 "$MIN" "[$AEUSD,$TAAPL]" "$ME_HEX" $(($(date +%s) + 600)) --private-key "$PK" --rpc-url "$AEVA_EVM" --json | jq -r '"swap: status \(.status), tx \(.transactionHash)"'swap: status 0x1, tx 0x…MIN accepts at most 1 % slippage from the quote. The pool now holds 10 more
aeUSD, and you hold more tAAPL, natively, as rights:
curl -s "$AEVA_INDEXER/v1/accounts/$ME" | jq -r --arg a 84f50ea918e0c15c9321362330d513593fd4536b807e1b40a39682dab3e21b55 '.assets[] | select(.asset_id == $a) | "tAAPL: \(.whole_units) units of every kind"'tAAPL: <units> units of every kindTo swap native AEVA, use swapExactETHForTokens with WAEVA as the first hop,
as on any Uniswap v2. The app’s /swap and
/pools pages do the same from MetaMask, and
add liquidity.
Notes for contract authors
- AE-20 tokens are precompiles: there is no code at their address. Solidity
before 0.8.10 checks
extcodesizebefore a high-level call and reverts, so the testnet DEX readsbalanceOfwith a low-levelstaticcall. See The EVM facade & AE-20. - A pool is an ordinary account on the native side: it holds rights, accrues their income, and every transfer in or out passes the asset’s policy.