Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to OP Sepolia testnet #10

Merged
merged 9 commits into from
May 7, 2024
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6,430 changes: 3,411 additions & 3,019 deletions app/package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions app/package.json
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-alert-dialog": "^1.0.4",
"@radix-ui/react-collapsible": "^1.0.3",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-dialog": "1.0.4",
"@radix-ui/react-dropdown-menu": "^2.0.5",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-scroll-area": "^1.0.4",
@@ -39,8 +39,8 @@
"server-only": "^0.0.1",
"tailwind-merge": "^1.12.0",
"tailwindcss-animate": "^1.0.5",
"viem": "~0.3.24",
"wagmi": "^1.4.12",
"viem": "~1.21.4",
"wagmi": "^1.4.13",
"zod": "^3.22.1"
},
"devDependencies": {
11 changes: 0 additions & 11 deletions app/public/matic.svg

This file was deleted.

6 changes: 6 additions & 0 deletions app/public/optimism.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 0 additions & 10 deletions app/public/polygon.svg

This file was deleted.

12 changes: 6 additions & 6 deletions app/src/components/bet-card.tsx
Original file line number Diff line number Diff line change
@@ -93,7 +93,7 @@ export default function BetCard({
<div className="flex items-center">
<p>{`${formatEther(
prediction.amount ?? BigInt(0),
)} MATIC`}</p>
)} ETH`}</p>
</div>
) : gameData.resolved && isPredictionCorrect ? (
<div className="flex items-center">
@@ -105,7 +105,7 @@ export default function BetCard({
/>
<p>{`+${formatEther(
calculatedWinnings ?? BigInt(0),
)} MATIC`}</p>
)} ETH`}</p>
</div>
) : gameData.resolved && !isPredictionCorrect ? (
<div className="flex items-center">
@@ -117,7 +117,7 @@ export default function BetCard({
/>
<p>{`-${formatEther(
prediction.amount ?? BigInt(0),
)} MATIC`}</p>
)} ETH`}</p>
</div>
) : (
<div className="flex items-center">
@@ -129,7 +129,7 @@ export default function BetCard({
/>
<p>{`+${formatEther(
calculatedWinnings ?? BigInt(0),
)} MATIC`}</p>
)} ETH`}</p>
</div>
)}
</div>
@@ -164,12 +164,12 @@ export default function BetCard({
)}
{txHash && (
<a
href={`https://mumbai.polygonscan.com/tx/${txHash}`}
href={`https://sepolia-optimism.etherscan.io/tx/${txHash}`}
target="_blank"
rel="noreferrer"
>
<Button className="w-full border-2 border-border bg-background text-base font-medium leading-4 text-foreground hover:bg-background/90 hover:text-muted-foreground">
View Polygonscan
View Etherscan
</Button>
</a>
)}
8 changes: 4 additions & 4 deletions app/src/components/bet-slip-card.tsx
Original file line number Diff line number Diff line change
@@ -125,10 +125,10 @@ export default function BetslipCard({
<FormControl>
<div className="flex space-x-[8px] rounded-[8px] bg-secondary px-4 py-3">
<Image
src="/matic.svg"
src="/optimism.svg"
width={16}
height={16}
alt="matic"
alt="optimism"
/>
<Input
disabled={isLoading}
@@ -158,10 +158,10 @@ export default function BetslipCard({
<FormControl>
<div className="flex space-x-[8px] rounded-[8px] bg-secondary px-4 py-3">
<Image
src="/matic.svg"
src="/optimism.svg"
width={16}
height={16}
alt="matic"
alt="optimism"
/>
<Input
disabled
8 changes: 4 additions & 4 deletions app/src/components/claim-button.tsx
Original file line number Diff line number Diff line change
@@ -144,22 +144,22 @@ const ClaimButton = ({
<DialogDescription className="font-[450] text-muted-foreground">
{`You are transferring ${formatEther(
calculatedWinnings,
)} MATIC from Polygon Mumbai to Avalanche Fuji.`}
)} ETH from Optimism Sepolia to Avalanche Fuji.`}
</DialogDescription>
</DialogHeader>
<div className="flex w-full items-end space-x-4">
<div>
<span className="mb-4 text-[14px] leading-4">From</span>
<div className="flex items-center space-x-[8px] rounded-[8px] bg-primary px-4 py-3">
<Image
src="/matic.svg"
src="/optimism.svg"
width={24}
height={24}
alt="matic"
alt="optimism"
/>
<div className="text-sm font-[450] leading-4">{`${formatEther(
calculatedWinnings,
)} MATIC`}</div>
)} ETH`}</div>
</div>
</div>
<Image
8 changes: 4 additions & 4 deletions app/src/components/main-nav.tsx
Original file line number Diff line number Diff line change
@@ -53,7 +53,7 @@ export default function MainNav() {
</AccordionTrigger>
<AccordionContent>
<p className="text-secondary-foreground">
Built on top of Polygon testnet and Chainlink, this dApp
Built on top of Optimism Sepolia testnet and Chainlink, this dApp
enables users to interact with real time sport data, receive
automatic payouts, and transfer tokens across chains.
</p>
@@ -62,13 +62,13 @@ export default function MainNav() {
</Accordion>

<a
href="https://faucet.polygon.technology"
href="https://faucets.chain.link/optimism-sepolia"
target="_blank"
rel="noreferrer"
className="mt-4 flex items-center space-x-[8px] text-base font-bold leading-4 hover:underline hover:brightness-125"
>
<Image src="/polygon.svg" width={16} height={16} alt="polygon" />
<span>Get testnet MATIC</span>
<Image src="/optimism.svg" width={16} height={16} alt="optimism" />
<span>Get testnet ETH</span>
<Image
src="/external-link.svg"
width={12}
4 changes: 2 additions & 2 deletions app/src/components/place-bet-button.tsx
Original file line number Diff line number Diff line change
@@ -40,12 +40,12 @@ export default function PlaceBetButton({
return
}
if (predictions.some((p) => p.wager! < minWager)) {
setError(`Minimum bet amount is ${minWager} MATIC`)
setError(`Minimum bet amount is ${minWager} ETH`)
setTimeout(() => setError(null), 3000)
return
}
if (predictions.some((p) => p.wager! > maxWager)) {
setError(`Maximum bet amount is ${maxWager} MATIC`)
setError(`Maximum bet amount is ${maxWager} ETH`)
setTimeout(() => setError(null), 3000)
return
}
2 changes: 1 addition & 1 deletion app/src/components/user-balance.tsx
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ const UserBalance = () => {
<div className="font-bold">
{address ? (
<div className="flex items-center space-x-[4px]">
<Image src="/matic.svg" width={16} height={16} alt="matic" />
<Image src="/optimism.svg" width={16} height={16} alt="optimism" />
<span className="text-xs">{`${data?.formatted.slice(0, 4)} ${
data?.symbol
}`}</span>
4 changes: 2 additions & 2 deletions app/src/wagmi.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { getDefaultWallets } from '@rainbow-me/rainbowkit'
import { configureChains, createConfig } from 'wagmi'
import { polygonMumbai } from 'wagmi/chains'
import { optimismSepolia } from 'wagmi/chains'
import { alchemyProvider } from 'wagmi/providers/alchemy'
import { publicProvider } from 'wagmi/providers/public'

const { chains, publicClient, webSocketPublicClient } = configureChains(
[polygonMumbai],
[optimismSepolia],
[
alchemyProvider({ apiKey: process.env.NEXT_PUBLIC_ALCHEMY_API_KEY! }),
publicProvider(),
13 changes: 10 additions & 3 deletions contracts/README.md
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ All required configuration for supported networks is located in the [`networks.j
2. Use the command `npx env-enc set` to set the required environment variables (see [Environment Variable Management](#environment-variable-management)):
- _GITHUB_API_TOKEN_ for your Github token obtained from step 3
- _PRIVATE_KEY_ for your development wallet
- _POLYGON_MUMBAI_RPC_URL_, _ETHEREUM_SEPOLIA_RPC_URL_, _AVALANCHE_FUJI_RPC_URL_ for the network that you intend to use
- _OPTIMISM_SEPOLIA_RPC_URL_, _AVALANCHE_FUJI_RPC_URL_ for the network that you intend to use
- _API_KEY_ for the sports results API. Obtain one [here](https://dashboard.api-football.com/register).
3. If desired, the `<explorer>_API_KEY` can be set in order to verify contracts, along with any values used in the _secrets_ object in _Functions-request-config.js_ such as `COINMARKETCAP_API_KEY`.<br><br>
4. Test an end-to-end request and fulfillment locally by simulating it using:<br>`npx hardhat functions-simulate`<br><br>
@@ -45,11 +45,18 @@ All required configuration for supported networks is located in the [`networks.j
- Your wallet has a sufficient native token balance on both networks for the deployment and transaction fees.
- Yoyr wallet LINK balance is sufficient to fund the contract so it can pay the CCIP transfer fees. The amount is specified in the `networks.js` file under each network `fundAmount` property.
- `<explorer>_API_KEY` is set if using `--verify true`, depending on which network is used.<br><br>
8. In order for the registered games in the contract to be updated with results from the sports API automatically, the game contract must be registered as an upkeep. Follow the steps at [https://automation.chain.link](https://automation.chain.link).<br>**Note**: Make sure the gas limit is set to 1,000,000.<br><br>
8. In order for the registered games in the contract to be updated with results from the sports API automatically, the game contract must be registered as an upkeep. Follow the steps at [https://automation.chain.link](https://automation.chain.link).<br>**Note**: Make sure the gas limit is set to 3,000,000.<br><br>

## Deployments

Polygon Mumbai
Optimism Sepolia

- Game contract: [`0x31015944A2719Da19531Ced7ed72e9DD6761A478`](https://sepolia-optimism.etherscan.io/address/0x31015944A2719Da19531Ced7ed72e9DD6761A478)
- Functions subscription: [`177`](https://functions.chain.link/optimism-sepolia/177)
- Automation upkeep: [`112557321685295299629771618321084972343709241235617405730580214266603599595202`](https://automation.chain.link/optimism-sepolia/112557321685295299629771618321084972343709241235617405730580214266603599595202)
- CCIP Token Receiver contract on Avalanche Fuji: [`0xB9bb8BfD3540E44b8eAaC9d5Dfc31B9D8E898C03`](https://testnet.snowtrace.io/address/0xB9bb8BfD3540E44b8eAaC9d5Dfc31B9D8E898C03)

Polygon Mumbai (Deprecated)

- Game contract: [`0x837acF842c9D99004A4b4fa1C250Fe3ca0c3ce63`](https://mumbai.polygonscan.com/address/0x837acF842c9D99004A4b4fa1C250Fe3ca0c3ce63)
- Functions subscription: [`1328`](https://functions.chain.link/mumbai/1328)
7 changes: 3 additions & 4 deletions contracts/contracts/ccip/NativeTokenSender.sol
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
pragma solidity ^0.8.0;

import {ProgrammableTokenSender} from "./ProgrammableTokenSender.sol";
import {ISwapRouter} from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import {IV3SwapRouter} from "../interfaces/IV3SwapRouter.sol";

/// @title NativeTokenSender
/// @notice Sends native token transfer request to another chain
@@ -82,17 +82,16 @@ abstract contract NativeTokenSender is ProgrammableTokenSender {
/// @param _nativeTokenAmount The amount of native token to swap
/// @return exchangeTokenAmount The amount of exchange token received
function _swapNativeToExchangeToken(uint256 _nativeTokenAmount) internal returns (uint256 exchangeTokenAmount) {
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
IV3SwapRouter.ExactInputSingleParams memory params = IV3SwapRouter.ExactInputSingleParams({
tokenIn: weth9Token,
tokenOut: exchangeToken,
fee: POOL_FEE,
recipient: address(this),
deadline: block.timestamp,
amountIn: _nativeTokenAmount,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
});
exchangeTokenAmount = ISwapRouter(uniswapV3Router).exactInputSingle{value: _nativeTokenAmount}(params);
exchangeTokenAmount = IV3SwapRouter(uniswapV3Router).exactInputSingle{value: _nativeTokenAmount}(params);
}

// OWNER
69 changes: 69 additions & 0 deletions contracts/contracts/interfaces/IV3SwapRouter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;

import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';

/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface IV3SwapRouter is IUniswapV3SwapCallback {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}

/// @notice Swaps `amountIn` of one token for as much as possible of another token
/// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance,
/// and swap the entire amount, enabling contracts to send tokens before calling this function.
/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
/// @return amountOut The amount of the received token
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);

struct ExactInputParams {
bytes path;
address recipient;
uint256 amountIn;
uint256 amountOutMinimum;
}

/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
/// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance,
/// and swap the entire amount, enabling contracts to send tokens before calling this function.
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
/// @return amountOut The amount of the received token
function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);

struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}

/// @notice Swaps as little as possible of one token for `amountOut` of another token
/// that may remain in the router after the swap.
/// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
/// @return amountIn The amount of the input token
function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);

struct ExactOutputParams {
bytes path;
address recipient;
uint256 amountOut;
uint256 amountInMaximum;
}

/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
/// that may remain in the router after the swap.
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
/// @return amountIn The amount of the input token
function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}
1 change: 0 additions & 1 deletion contracts/contracts/test/MockSwapRouter.sol
Original file line number Diff line number Diff line change
@@ -7,7 +7,6 @@ contract MockSwapRouter {
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
11 changes: 11 additions & 0 deletions contracts/hardhat.config.js
Original file line number Diff line number Diff line change
@@ -61,7 +61,18 @@ module.exports = {
sepolia: networks.ethereumSepolia.verifyApiKey,
polygonMumbai: networks.polygonMumbai.verifyApiKey,
avalancheFujiTestnet: networks.avalancheFuji.verifyApiKey,
optimismSepolia: networks.optimismSepolia.verifyApiKey,
},
customChains: [
{
network: "optimismSepolia",
chainId: 11155420,
urls: {
apiURL: "https://api-sepolia-optimistic.etherscan.io/api",
browserURL: "https://sepolia-optimism.etherscan.io/",
},
},
],
},
gasReporter: {
enabled: REPORT_GAS,
23 changes: 21 additions & 2 deletions contracts/networks.js
Original file line number Diff line number Diff line change
@@ -41,15 +41,34 @@ const networks = {
nativeCurrencySymbol: "MATIC",
linkToken: "0x326C977E6efc84E512bB9C30f76E30c160eD06FB",
linkPriceFeed: "0x12162c3E810393dEC01362aBf156D7ecf6159528", // LINK/MATIC
functionsRouter: '0x6E2dc0F9DB014aE19888F539E59285D2Ea04244C',
functionsDonId: 'fun-polygon-mumbai-1',
functionsRouter: "0x6E2dc0F9DB014aE19888F539E59285D2Ea04244C",
functionsDonId: "fun-polygon-mumbai-1",
ccipRouter: "0x1035CabC275068e0F4b745A29CEDf38E13aF41b1",
ccipChainSelector: "12532609583862916517",
ccipTestToken: "0xf1E3A5842EeEF51F2967b3F05D45DD4f4205FF40",
uniswapV3Router: "0xE592427A0AEce92De3Edee1F18E0157C05861564",
weth9: "0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889",
fundAmount: "1", // 1 LINK
},
optimismSepolia: {
url: process.env.OPTIMISM_SEPOLIA_RPC_URL || "UNSET",
gasPrice: undefined,
accounts: PRIVATE_KEY !== undefined ? [PRIVATE_KEY] : [],
verifyApiKey: process.env.OP_ETHERSCAN_API_KEY || "UNSET",
chainId: 11155420,
confirmations: DEFAULT_VERIFICATION_BLOCK_CONFIRMATIONS,
nativeCurrencySymbol: "ETH",
linkToken: "0xE4aB69C077896252FAFBD49EFD26B5D171A32410",
linkPriceFeed: "0x98EeB02BC20c5e7079983e8F0D0D839dFc8F74fA", // LINK/ETH
functionsRouter: "0xC17094E3A1348E5C7544D4fF8A36c28f2C6AAE28",
functionsDonId: "fun-optimism-sepolia-1",
ccipRouter: "0x114A20A10b43D4115e5aeef7345a1A71d2a60C57",
ccipChainSelector: "5224473277236331295",
ccipTestToken: "0x8aF4204e30565DF93352fE8E1De78925F6664dA7",
uniswapV3Router: "0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4",
weth9: "0x4200000000000000000000000000000000000006",
fundAmount: "1", // 1 LINK
},
avalancheFuji: {
url: process.env.AVALANCHE_FUJI_RPC_URL || "UNSET",
gasPrice: undefined,