diff --git a/frontend/claim_sdk/testWallets.ts b/frontend/claim_sdk/testWallets.ts
index 34a1475c..ea206347 100644
--- a/frontend/claim_sdk/testWallets.ts
+++ b/frontend/claim_sdk/testWallets.ts
@@ -11,6 +11,7 @@ import {
import { ethers } from 'ethers'
import fs from 'fs'
import { AminoSignResponse, Secp256k1HdWallet } from '@cosmjs/amino'
+import { HdPath, stringToPath } from '@cosmjs/crypto'
import { makeADR36AminoSignDoc } from '@keplr-wallet/cosmos'
import NodeWallet from '@coral-xyz/anchor/dist/cjs/nodewallet'
import { Keypair, PublicKey } from '@solana/web3.js'
@@ -90,9 +91,10 @@ export async function loadTestWallets(): Promise<
result['sui'] = [TestSuiWallet.fromKeyfile(suiPrivateKeyPath)]
result['aptos'] = [TestAptosWallet.fromKeyfile(aptosPrivateKeyPath)]
result['cosmwasm'] = [
- await TestCosmWasmWallet.fromKeyFile(cosmosPrivateKeyPath, 'sei'),
await TestCosmWasmWallet.fromKeyFile(cosmosPrivateKeyPath, 'osmo'),
- await TestCosmWasmWallet.fromKeyFile(cosmosPrivateKeyPath, 'neutron'),
+ await TestCosmWasmWallet.fromKeyFile(cosmosPrivateKeyPath, 'terra', [
+ stringToPath("m/44'/330'/0'/0/0"),
+ ]),
]
result['injective'] = [TestEvmWallet.fromKeyfile(cosmosPrivateKeyPath, true)]
@@ -150,14 +152,15 @@ export class TestCosmWasmWallet implements TestWallet {
*/
static async fromKeyFile(
keyFile: string,
- chainId?: string
+ prefix?: string,
+ hdPaths?: HdPath[]
): Promise {
const jsonContent = fs.readFileSync(keyFile, 'utf8')
const mnemonic = JSON.parse(jsonContent).mnemonic
const wallet: Secp256k1HdWallet = await Secp256k1HdWallet.fromMnemonic(
mnemonic,
- chainId ? { prefix: chainId } : {}
+ { prefix, hdPaths }
)
const { address: addressStr } = (await wallet.getAccounts())[0]
diff --git a/frontend/components/DiscordButton.tsx b/frontend/components/DiscordButton.tsx
index 6a87f19e..79146d6b 100644
--- a/frontend/components/DiscordButton.tsx
+++ b/frontend/components/DiscordButton.tsx
@@ -19,7 +19,7 @@ const newTab = (url: string, title: string) => {
export function DiscordButton({ disableOnAuth }: DiscordButtonProps) {
// TODO update logic to get discord data from lambda function execution
- const { data = {}, status = '' } = {} as any;
+ const { data = {}, status = '' } = {} as any
const { logo, text } = useMemo(() => {
if (status === 'authenticated')
diff --git a/frontend/components/Ecosystem/index.tsx b/frontend/components/Ecosystem/index.tsx
index d10a98c0..8a595c63 100644
--- a/frontend/components/Ecosystem/index.tsx
+++ b/frontend/components/Ecosystem/index.tsx
@@ -10,8 +10,7 @@ export enum Ecosystem {
SUI = 'Sui',
INJECTIVE = 'Injective',
OSMOSIS = 'Osmosis',
- NEUTRON = 'Neutron',
- SEI = 'Sei',
+ TERRA = 'Terra',
DISCORD = 'Pyth Discord',
}
diff --git a/frontend/components/EcosystemConnectButton.tsx b/frontend/components/EcosystemConnectButton.tsx
index 4352af6b..6c06e73c 100644
--- a/frontend/components/EcosystemConnectButton.tsx
+++ b/frontend/components/EcosystemConnectButton.tsx
@@ -3,7 +3,6 @@ import { Ecosystem } from './Ecosystem'
import { AptosWalletButton } from './wallets/Aptos'
import { CosmosWalletButton } from './wallets/Cosmos'
import { EVMWalletButton } from './wallets/EVM'
-import { SeiWalletButton } from './wallets/Sei'
import { SolanaWalletButton } from './wallets/Solana'
import { SuiWalletButton } from './wallets/Sui'
@@ -29,10 +28,10 @@ export function EcosystemConnectButton({
isInjective={true}
/>
)
- case Ecosystem.NEUTRON:
+ case Ecosystem.TERRA:
return (
)
@@ -43,8 +42,6 @@ export function EcosystemConnectButton({
disableOnConnect={disableOnConnect}
/>
)
- case Ecosystem.SEI:
- return
case Ecosystem.SOLANA:
return
case Ecosystem.SUI:
diff --git a/frontend/components/modal/Disclaimer.tsx b/frontend/components/modal/Disclaimer.tsx
index 2cf54073..32b5a9df 100644
--- a/frontend/components/modal/Disclaimer.tsx
+++ b/frontend/components/modal/Disclaimer.tsx
@@ -29,9 +29,9 @@ export function Disclaimer({ onAgree, showModal }: DisclaimerProps) {
-
- You agree and acknowledge that you have the sole responsibility and
- liability for all taxes in connection with your participation in the
- Airdrop and you should consult a tax advisor.
+ You agree and acknowledge that you have the sole responsibility
+ and liability for all taxes in connection with your participation
+ in the Airdrop and you should consult a tax advisor.
-
You agree and acknowledge that you are solely responsible for
@@ -40,78 +40,78 @@ export function Disclaimer({ onAgree, showModal }: DisclaimerProps) {
-
You agree and acknowledge that you (a) may receive tokens for free
- via the Airdrop (other than applicable taxes, if any), (b) were not
- previously promised any tokens, and (c) took no action in
+ via the Airdrop (other than applicable taxes, if any), (b) were
+ not previously promised any tokens, and (c) took no action in
anticipation of or in reliance on receiving any tokens or an
Airdrop.
-
Your eligibility to receive Airdrop tokens or participate in the
- Airdrop is subject to our sole discretion. To the extent you believe
- you should have received any airdropped tokens based on any
- documentation or points system released by us from time to time,
- such documentation does not entitle you to any tokens or to
+ Airdrop is subject to our sole discretion. To the extent you
+ believe you should have received any airdropped tokens based on
+ any documentation or points system released by us from time to
+ time, such documentation does not entitle you to any tokens or to
participate in the Airdrop and therefore you have no claim for any
such tokens. You agree not to engage in any activities that are
- designed to obtain more Airdrop tokens than you are entitled to. You
- agree that you are the legal owner of the blockchain address that
- you use to access or participate in the Airdrop.
+ designed to obtain more Airdrop tokens than you are entitled to.
+ You agree that you are the legal owner of the blockchain address
+ that you use to access or participate in the Airdrop.
-
You agree and acknowledge that you are not a Prohibited Person and
you will not use a VPN or other tool to circumvent any geoblock or
other restrictions that we may have implemented for Airdrop
- recipients. Any such circumvention, or attempted circumvention, may
- permanently disqualify you from participation in the Airdrop in our
- discretion.
+ recipients. Any such circumvention, or attempted circumvention,
+ may permanently disqualify you from participation in the Airdrop
+ in our discretion.
-
- To participate in the Airdrop, you will need to connect a compatible
- third-party digital wallet (“Wallet”). By using a
- Wallet, you agree that you are using the Wallet under the terms and
- conditions of the applicable third-party provider of such Wallet.
- Wallets are not associated with, maintained by, supported by or
- affiliated with the Organization. When you interact with the
- Services, you retain control over your digital assets at all times.
- We accept no responsibility or liability to you in connection with
- your use of a Wallet, and we make no representations or warranties
- regarding how the Services will operate or be compatible with any
- specific Wallet.{" "}
+ To participate in the Airdrop, you will need to connect a
+ compatible third-party digital wallet (“Wallet”). By using a
+ Wallet, you agree that you are using the Wallet under the terms
+ and conditions of the applicable third-party provider of such
+ Wallet. Wallets are not associated with, maintained by, supported
+ by or affiliated with the Organization. When you interact with the
+ Services, you retain control over your digital assets at all
+ times. We accept no responsibility or liability to you in
+ connection with your use of a Wallet, and we make no
+ representations or warranties regarding how the Services will
+ operate or be compatible with any specific Wallet.{' '}
The private keys necessary to access the assets held in a Wallet
- are not held by the Organization. The Organization has no ability
- to help you access or recover your private keys and/or seed
- phrases for your Wallet. You are solely responsible for
+ are not held by the Organization. The Organization has no
+ ability to help you access or recover your private keys and/or
+ seed phrases for your Wallet. You are solely responsible for
maintaining the confidentiality of your private keys and you are
responsible for any transactions signed with your private keys
-
- You agree and acknowledge that if you are unable to claim an Airdrop
- due to technical bugs, smart contract issue, gas fees, wallet
- incompatibility, loss of access to a wallet or the keys thereto, or
- for any other reason, you will have no recourse or claim against us
- or our affiliates and that we and our affiliates will not bear any
- liability.
+ You agree and acknowledge that if you are unable to claim an
+ Airdrop due to technical bugs, smart contract issue, gas fees,
+ wallet incompatibility, loss of access to a wallet or the keys
+ thereto, or for any other reason, you will have no recourse or
+ claim against us or our affiliates and that we and our affiliates
+ will not bear any liability.
-
You agree and acknowledge that claiming an Airdrop may require
reliance on or an integration with third party products (e.g., a
wallet or an unaffiliated network or blockchain) that we do not
- control. In the event that you are unable to access such products or
- integrations, or if they fail for any reason, and you are unable to
- participate in an Airdrop or claim Airdrop tokens, you will have no
- recourse or claim against us or our affiliates and we and our
- affiliates will not bear any liability.
+ control. In the event that you are unable to access such products
+ or integrations, or if they fail for any reason, and you are
+ unable to participate in an Airdrop or claim Airdrop tokens, you
+ will have no recourse or claim against us or our affiliates and we
+ and our affiliates will not bear any liability.
-
You agree and acknowledge that the regulatory regime governing
blockchain technologies, cryptocurrencies and other digital assets
is uncertain, that new regulations or policies may materially
adversely affect the potential utility or value of such
- cryptocurrencies and digital assets, and that there are risks of new
- taxation related to the purchase or sale of cryptocurrencies and
- other digital assets.
+ cryptocurrencies and digital assets, and that there are risks of
+ new taxation related to the purchase or sale of cryptocurrencies
+ and other digital assets.
-
You agree and acknowledge that cryptocurrencies and other similar
@@ -120,17 +120,18 @@ export function Disclaimer({ onAgree, showModal }: DisclaimerProps) {
-
- These Airdrop Terms and the General Terms contain the entire agreement
- between you and the Organization regarding the Airdrop, and supersede
- all prior and contemporaneous understandings between the parties
- regarding the Airdrop. We may modify these Airdrop Terms from time to
- time in which case we will update the “Last Revised” date at the top
- of these Airdrop Terms. The updated Airdrop Terms will be effective as
- of the time of posting, or such later date as may be specified in the
- updated Airdrop Terms. Your continued access or participation in the
- Airdrop after the modifications have become effective will be deemed
- your acceptance of the modified Airdrop Terms.{" "}
+
+ These Airdrop Terms and the General Terms contain the entire
+ agreement between you and the Organization regarding the Airdrop,
+ and supersede all prior and contemporaneous understandings between
+ the parties regarding the Airdrop. We may modify these Airdrop Terms
+ from time to time in which case we will update the “Last Revised”
+ date at the top of these Airdrop Terms. The updated Airdrop Terms
+ will be effective as of the time of posting, or such later date as
+ may be specified in the updated Airdrop Terms. Your continued access
+ or participation in the Airdrop after the modifications have become
+ effective will be deemed your acceptance of the modified Airdrop
+ Terms.{' '}
diff --git a/frontend/components/wallets/Cosmos.tsx b/frontend/components/wallets/Cosmos.tsx
index c4965859..6c9544c1 100644
--- a/frontend/components/wallets/Cosmos.tsx
+++ b/frontend/components/wallets/Cosmos.tsx
@@ -10,7 +10,7 @@ import keplr from '@images/keplr.svg'
export const WALLET_NAME = 'keplr-extension'
-export type ChainName = 'osmosis' | 'neutron' | 'sei'
+export type ChainName = 'osmosis' | 'terra'
type CosmosWalletProviderProps = {
children: ReactNode
diff --git a/frontend/components/wallets/Sei.tsx b/frontend/components/wallets/Sei.tsx
deleted file mode 100644
index df43a0fa..00000000
--- a/frontend/components/wallets/Sei.tsx
+++ /dev/null
@@ -1,158 +0,0 @@
-import {
- ReactNode,
- createContext,
- useCallback,
- useContext,
- useEffect,
- useState,
-} from 'react'
-import { useChainWallet } from '@cosmos-kit/react-lite'
-import { WalletButton, WalletConnectedButton } from './WalletButton'
-
-import keplr from '@images/keplr.svg'
-import compass from '@images/compass.svg'
-
-type StoredWallet = 'keplr-extension' | 'compass-extension' | null
-
-type SeiContextType = {
- connectedSeiWallet: StoredWallet
- setConnectedSeiWallet: (wallet: StoredWallet) => void
-}
-const SeiContext = createContext
(undefined)
-
-const LOCAL_STORAGE_SEI_WALLET_KEY = 'sei-local-storage-connection-key'
-// we need a provider to be able to sync with local storage
-export function SeiProvider({ children }: { children: ReactNode }) {
- const [connectedWallet, setConnectedWallet] = useState(null)
-
- // On first render read the connected wallet name
- useEffect(() => {
- setConnectedWallet(
- localStorage.getItem(LOCAL_STORAGE_SEI_WALLET_KEY) as StoredWallet
- )
- }, [])
-
- const setConnectedSeiWallet = useCallback((wallet: StoredWallet) => {
- if (typeof window === 'undefined') return null
- if (wallet === null) {
- localStorage.removeItem(LOCAL_STORAGE_SEI_WALLET_KEY)
- return
- }
- localStorage.setItem(LOCAL_STORAGE_SEI_WALLET_KEY, wallet)
- setConnectedWallet(wallet)
- }, [])
-
- return (
-
- {children}
-
- )
-}
-
-export function useSeiWalletContext() {
- const ctx = useContext(SeiContext)
- if (ctx === undefined)
- throw new Error('Hook should be called under the provider')
-
- return ctx
-}
-
-type SeiWalletButtonProps = {
- disableOnConnect?: boolean
-}
-export function SeiWalletButton({ disableOnConnect }: SeiWalletButtonProps) {
- const compassChainWalletctx = useChainWallet('sei', 'compass-extension')
- const keplrChainWalletctx = useChainWallet('sei', 'keplr-extension')
- const [icon, setIcon] = useState()
- const { connectedSeiWallet, setConnectedSeiWallet } = useSeiWalletContext()
-
- // Cosmos wallets doesn't provide any autoconnect feature
- // Implementing it here
- // When this component will render, it will check a localStorage key
- // to know if the wallet was previously connected. If it was, it will
- // connect with it again. Else, will do nothing
- // We only have to do this check once the component renders.
- // See Line 84, 99 to know how we are storing the status locally
- useEffect(() => {
- if (connectedSeiWallet === 'keplr-extension') {
- keplrChainWalletctx.connect()
- } else if (connectedSeiWallet === 'compass-extension') {
- compassChainWalletctx.connect()
- }
- }, [])
-
- useEffect(() => {
- if (
- keplrChainWalletctx.isWalletConnected === true &&
- keplrChainWalletctx?.address !== undefined
- ) {
- setConnectedSeiWallet('keplr-extension')
- setIcon(keplr)
- } else if (
- compassChainWalletctx.isWalletConnected === true &&
- compassChainWalletctx?.address !== undefined
- ) {
- setConnectedSeiWallet('compass-extension')
- setIcon(compass)
- }
-
- if (
- keplrChainWalletctx.isWalletDisconnected &&
- compassChainWalletctx.isWalletDisconnected
- ) {
- setConnectedSeiWallet(null)
- setIcon(undefined)
- }
- }, [keplrChainWalletctx, compassChainWalletctx, setConnectedSeiWallet])
-
- return (
- {
- if (keplrChainWalletctx.isWalletNotExist) {
- window.open('https://www.keplr.app/download')
- } else keplrChainWalletctx.connect()
- },
- },
- {
- name: 'compass',
- icon: compass,
- onSelect: () => {
- if (compassChainWalletctx.isWalletNotExist) {
- window.open(
- 'https://chrome.google.com/webstore/detail/compass-wallet-for-sei/anokgmphncpekkhclmingpimjmcooifb'
- )
- } else compassChainWalletctx.connect()
- },
- },
- ]}
- walletConnectedButton={(address: string) => (
- {
- if (connectedSeiWallet === 'keplr-extension')
- keplrChainWalletctx.disconnect()
- else if (connectedSeiWallet === 'compass-extension')
- compassChainWalletctx.disconnect()
- }}
- address={address}
- disabled={disableOnConnect}
- icon={icon}
- />
- )}
- />
- )
-}
diff --git a/frontend/hooks/useGetEcosystemIdentity.ts b/frontend/hooks/useGetEcosystemIdentity.ts
index e30019b0..c2247994 100644
--- a/frontend/hooks/useGetEcosystemIdentity.ts
+++ b/frontend/hooks/useGetEcosystemIdentity.ts
@@ -8,7 +8,6 @@ import {
import { Ecosystem } from '@components/Ecosystem'
import { useCallback } from 'react'
-import { useSeiWalletContext } from '@components/wallets/Sei'
import { getInjectiveAddress } from '../utils/getInjectiveAddress'
// It will return a function that can be used to get the identity of a given ecosystem
@@ -18,10 +17,7 @@ export function useGetEcosystemIdentity() {
const aptosAddress = useAptosAddress()
const evmAddress = useEVMAddress()
const osmosisAddress = useCosmosAddress('osmosis')
- const neutronAddress = useCosmosAddress('neutron')
-
- const { connectedSeiWallet } = useSeiWalletContext()
- const seiAddress = useCosmosAddress('sei', connectedSeiWallet ?? undefined)
+ const terraAddress = useCosmosAddress('terra')
const solanaAddress = useSolanaAddress()
const suiAddress = useSuiAddress()
// TODO update logic to get discord data from lambda function execution
@@ -40,14 +36,11 @@ export function useGetEcosystemIdentity() {
case Ecosystem.INJECTIVE:
return evmAddress ? getInjectiveAddress(evmAddress) : undefined
- case Ecosystem.NEUTRON:
- return neutronAddress
-
case Ecosystem.OSMOSIS:
return osmosisAddress
- case Ecosystem.SEI:
- return seiAddress
+ case Ecosystem.TERRA:
+ return terraAddress
case Ecosystem.SOLANA:
return solanaAddress
@@ -63,9 +56,8 @@ export function useGetEcosystemIdentity() {
aptosAddress,
data?.user?.hashedUserId,
evmAddress,
- neutronAddress,
osmosisAddress,
- seiAddress,
+ terraAddress,
solanaAddress,
suiAddress,
]
diff --git a/frontend/hooks/useSignMessage.tsx b/frontend/hooks/useSignMessage.tsx
index 6af9e9ad..761425cb 100644
--- a/frontend/hooks/useSignMessage.tsx
+++ b/frontend/hooks/useSignMessage.tsx
@@ -15,7 +15,6 @@ import { Ecosystem } from '@components/Ecosystem'
import { fetchDiscordSignedMessage } from 'utils/api'
import { useTokenDispenserProvider } from './useTokenDispenserProvider'
import { ChainName } from '@components/wallets/Cosmos'
-import { useSeiWalletContext } from '@components/wallets/Sei'
// SignMessageFn signs the message and returns it.
// It will return undefined:
@@ -188,12 +187,7 @@ export function useSignMessage(ecosystem: Ecosystem): SignMessageFn {
const aptosSignMessageFn = useAptosSignMessage()
const evmSignMessageFn = useEVMSignMessage()
const osmosisSignMessageFn = useCosmosSignMessage('osmosis')
- const neutronSignMessageFn = useCosmosSignMessage('neutron')
- const { connectedSeiWallet } = useSeiWalletContext()
- const seiSignMessageFn = useCosmosSignMessage(
- 'sei',
- connectedSeiWallet ?? undefined
- )
+ const terraSignMessageFn = useCosmosSignMessage('terra')
const suiSignMessageFn = useSuiSignMessage()
const solanaSignMessageFn = useSolanaSignMessage()
const discordSignMessageFn = useDiscordSignMessage()
@@ -205,12 +199,10 @@ export function useSignMessage(ecosystem: Ecosystem): SignMessageFn {
return evmSignMessageFn
case Ecosystem.INJECTIVE:
return evmSignMessageFn
- case Ecosystem.NEUTRON:
- return neutronSignMessageFn
+ case Ecosystem.TERRA:
+ return terraSignMessageFn
case Ecosystem.OSMOSIS:
return osmosisSignMessageFn
- case Ecosystem.SEI:
- return seiSignMessageFn
case Ecosystem.SOLANA:
return solanaSignMessageFn
case Ecosystem.SUI:
diff --git a/frontend/integration/utils.ts b/frontend/integration/utils.ts
index d8fe9e49..7588d8fa 100644
--- a/frontend/integration/utils.ts
+++ b/frontend/integration/utils.ts
@@ -37,7 +37,7 @@ export const EVM_CHAINS = [
export type SOLANA_SOURCES = 'nft' | 'defi'
-export type EvmChains = (typeof EVM_CHAINS)[number]
+export type EvmChains = typeof EVM_CHAINS[number]
export type EvmBreakdownRow = {
chain: string
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 106c7b4a..c195bc21 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -58,7 +58,7 @@
"node-pg-migrate": "^6.2.2",
"papaparse": "^5.4.1",
"postcss": "^8.4.5",
- "prettier": "^2.7.1",
+ "prettier": "2.7.1",
"prettier-plugin-tailwindcss": "^0.1.1",
"tailwindcss": "^3.0.7",
"ts-jest": "^29.1.1",
@@ -23048,9 +23048,9 @@
}
},
"node_modules/prettier": {
- "version": "2.8.8",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
- "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
+ "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
diff --git a/frontend/package.json b/frontend/package.json
index 1031e73a..e1d15b39 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -8,7 +8,8 @@
"test:ci": "./scripts/setup.sh --test --no-postgres",
"serve": "serve out",
"start": "next start",
- "datadog": "ts-node ./scripts/datadog.ts"
+ "datadog": "ts-node ./scripts/datadog.ts",
+ "prettier": "prettier . --write"
},
"dependencies": {
"@aptos-labs/wallet-adapter-react": "^1.2.2",
@@ -64,7 +65,7 @@
"node-pg-migrate": "^6.2.2",
"papaparse": "^5.4.1",
"postcss": "^8.4.5",
- "prettier": "^2.7.1",
+ "prettier": "2.7.1",
"prettier-plugin-tailwindcss": "^0.1.1",
"tailwindcss": "^3.0.7",
"ts-jest": "^29.1.1",
diff --git a/frontend/pages/_app.tsx b/frontend/pages/_app.tsx
index fd16c670..c8242c49 100644
--- a/frontend/pages/_app.tsx
+++ b/frontend/pages/_app.tsx
@@ -10,16 +10,12 @@ import { CosmosWalletProvider } from '@components/wallets/Cosmos'
import { EcosystemProviders } from '@components/Ecosystem'
import '../styles/globals.css'
-import { SeiProvider } from '@components/wallets/Sei'
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
import { Layout } from '@components/Layout'
import { Disclaimer } from '@components/modal/Disclaimer'
import Script from 'next/script'
-import {
- PathnameStore,
- resetOnVersionMismatch,
-} from 'utils/store'
+import { PathnameStore, resetOnVersionMismatch } from 'utils/store'
function useRedirect(isVersionChecked: boolean) {
// We are fetching it here and not in useEffect
@@ -89,44 +85,42 @@ const App: FC = ({ Component, pageProps }: AppProps) => {
`}
{isVersionChecked ? (
-
-
-
-
-
-
- {/* WARN: EcosystemProviders might use wallet provider addresses and hence
+
+
+
+
+
+ {/* WARN: EcosystemProviders might use wallet provider addresses and hence
They should be inside all those providers. */}
-
-
-
-
-
-
- {
- setDisclaimerWasRead(true)
- }}
- />
-
-
-
-
-
-
-
+
+
+
+
+
+
+ {
+ setDisclaimerWasRead(true)
+ }}
+ />
+
+
+
+
+
+
) : (
<>>
)}
diff --git a/frontend/scripts/populate_from_csv.ts b/frontend/scripts/populate_from_csv.ts
new file mode 100644
index 00000000..8cf12a2e
--- /dev/null
+++ b/frontend/scripts/populate_from_csv.ts
@@ -0,0 +1,587 @@
+import { Keypair, PublicKey } from '@solana/web3.js'
+import { TokenDispenserProvider } from '../claim_sdk/solana'
+import { envOrErr } from '../claim_sdk/index'
+import {
+ EVM_CHAINS,
+ EvmChains,
+ SolanaBreakdownRow,
+ addClaimInfosToDatabase,
+ addEvmBreakdownsToDatabase,
+ addSolanaBreakdownsToDatabase,
+ clearDatabase,
+ getDatabasePool,
+} from '../utils/db'
+import fs from 'fs'
+import Papa from 'papaparse'
+
+import {
+ ClaimInfo,
+ Ecosystem,
+ Ecosystems,
+ getMaxAmount,
+} from '../claim_sdk/claim'
+import BN from 'bn.js'
+import NodeWallet from '@coral-xyz/anchor/dist/cjs/nodewallet'
+import { EvmBreakdownRow } from '../utils/db'
+import assert from 'assert'
+import path from 'path'
+import { hashDiscordUserId } from '../utils/hashDiscord'
+import { DISCORD_HASH_SALT, loadFunderWallet } from '../claim_sdk/testWallets'
+
+const DEBUG = true
+const pool = getDatabasePool()
+
+// The config is read from these env variables
+const ENDPOINT = envOrErr('ENDPOINT')
+const PROGRAM_ID = envOrErr('PROGRAM_ID')
+const DISPENSER_GUARD = Keypair.fromSecretKey(
+ new Uint8Array(JSON.parse(envOrErr('DISPENSER_GUARD')))
+)
+const FUNDER_KEYPAIR = Keypair.fromSecretKey(
+ new Uint8Array(JSON.parse(envOrErr('FUNDER_KEYPAIR')))
+)
+const DEPLOYER_WALLET = Keypair.fromSecretKey(
+ new Uint8Array(JSON.parse(envOrErr('DEPLOYER_WALLET')))
+)
+const PYTH_MINT = new PublicKey(envOrErr('PYTH_MINT'))
+const PYTH_TREASURY = new PublicKey(envOrErr('PYTH_TREASURY'))
+
+const CSV_DIR = envOrErr('CSV_DIR')
+const DEFI_CLAIMS = 'defi.csv'
+const DEFI_DEV_CLAIMS = 'defi_dev.csv'
+
+const DISCORD_CLAIMS = 'discord.csv'
+const DISCORD_DEV_CLAIMS = 'discord_dev.csv'
+
+const NFT_CLAIMS = 'nft.csv'
+
+const COSMWASM_CHAIN_LIST = ['terra', 'osmosis']
+
+function checkClaimsMatchEvmBreakdown(
+ claimInfos: ClaimInfo[],
+ evmBreakDowns: EvmBreakdownRow[]
+) {
+ const evmClaimInfos = claimInfos.filter((claimInfo) => {
+ return claimInfo.ecosystem === 'evm'
+ })
+ const evmClaimInfoAddrSet = new Set(
+ evmClaimInfos.map((claimInfo) => claimInfo.identity)
+ )
+
+ const sum: { [identity: string]: BN } = {}
+ for (const evmBreakDownRow of evmBreakDowns) {
+ if (sum[evmBreakDownRow.identity] == undefined) {
+ sum[evmBreakDownRow.identity] = new BN(0)
+ }
+ sum[evmBreakDownRow.identity] = sum[evmBreakDownRow.identity].add(
+ evmBreakDownRow.amount
+ )
+ }
+
+ assert(
+ Object.keys(sum).length === evmClaimInfos.length,
+ `
+ Number of evm identities in CSV file does not match number of identities in evm_breakdowns table.
+ sum: ${Object.keys(sum).length}
+ evmClaimInfos.length: ${evmClaimInfos.length}
+ evmClaimInfoAddrSet.length: ${evmClaimInfoAddrSet.size}
+ `
+ )
+
+ for (const evmClaim of evmClaimInfos) {
+ assert(
+ sum[evmClaim.identity].eq(evmClaim.amount),
+ `Breakdown for ${evmClaim.identity} does not match total amount`
+ )
+ }
+}
+
+function checkClaimsMatchSolanaBreakdown(
+ claimInfos: ClaimInfo[],
+ solanaBreakdownRows: SolanaBreakdownRow[]
+) {
+ const sum: { [identity: string]: BN } = {}
+ for (const solanaBreakdownRow of solanaBreakdownRows) {
+ if (sum[solanaBreakdownRow.identity] == undefined) {
+ sum[solanaBreakdownRow.identity] = new BN(0)
+ }
+ sum[solanaBreakdownRow.identity] = sum[solanaBreakdownRow.identity].add(
+ solanaBreakdownRow.amount
+ )
+ }
+ const solanaClaims = claimInfos.filter(
+ (claimInfo) => claimInfo.ecosystem === 'solana'
+ )
+ assert(
+ Object.keys(sum).length === solanaClaims.length,
+ 'Number of solana identities in CSV file does not match number of identities in solana_breakdowns table'
+ )
+
+ for (const solanaClaim of solanaClaims) {
+ assert(
+ sum[solanaClaim.identity].eq(solanaClaim.amount),
+ `Breakdown for ${solanaClaim.identity} does not match total amount`
+ )
+ }
+}
+
+function parseCsvs() {
+ // parse defi csvs
+ const groupedDefiAddresses = parseDefiCsv(DEFI_CLAIMS)
+ const groupedDefiDevAddresses = parseDefiCsv(DEFI_DEV_CLAIMS)
+
+ groupedDefiDevAddresses.forEach((devChainsAndAllocs, key) => {
+ const curValues = groupedDefiAddresses.get(key)
+ if (curValues) {
+ // skip duplicate identity + chain from defi_dev.csv
+ const curChainsForAddr = curValues.map((row) => row[0])
+ const deduped = devChainsAndAllocs.filter(([chain, alloc]) => {
+ const isUniqueDevAddr = !curChainsForAddr.includes(chain)
+ if (!isUniqueDevAddr) {
+ console.log(
+ `skipping dev claim for ${chain} address ${key} because it is already in defi.csv`
+ )
+ }
+ return isUniqueDevAddr
+ })
+ groupedDefiAddresses.set(key, [...curValues, ...deduped])
+ } else {
+ groupedDefiAddresses.set(key, devChainsAndAllocs)
+ }
+ })
+
+ // for each grouped address, if multiple values then all must be in evm chainlist
+ const evmBreakdownAddresses = new Map()
+
+ const claimInfos: ClaimInfo[] = []
+ const solanaBreakdownData: Map = new Map()
+
+ groupedDefiAddresses.forEach((chainsAndAllocs, key) => {
+ // only evm chains should have multiple values from defi csv files
+ if (chainsAndAllocs.length > 1) {
+ assert(
+ chainsAndAllocs.every(([chain, alloc]) =>
+ EVM_CHAINS.includes(chain as EvmChains)
+ ),
+ `Address ${key} has multiple values but not all are in evmChainList. chains: ${JSON.stringify(
+ chainsAndAllocs.map((row) => row[0])
+ )}`
+ )
+ evmBreakdownAddresses.set(key, chainsAndAllocs)
+ } else if (EVM_CHAINS.includes(chainsAndAllocs[0][0] as EvmChains)) {
+ evmBreakdownAddresses.set(key, chainsAndAllocs)
+ } else if (COSMWASM_CHAIN_LIST.includes(chainsAndAllocs[0][0])) {
+ claimInfos.push(
+ new ClaimInfo(
+ 'cosmwasm',
+ key,
+ truncateAllocation(chainsAndAllocs[0][1])
+ )
+ )
+ } else {
+ assert(
+ Ecosystems.includes(chainsAndAllocs[0][0] as Ecosystem),
+ `Unknown ecosystem detected for identity ${key} - ${chainsAndAllocs[0]}`
+ )
+ if (chainsAndAllocs[0][0] === 'solana') {
+ solanaBreakdownData.set(key, [
+ {
+ source: 'defi',
+ identity: key,
+ amount: truncateAllocation(chainsAndAllocs[0][1]),
+ },
+ ])
+ } else {
+ claimInfos.push(
+ new ClaimInfo(
+ chainsAndAllocs[0][0] as Ecosystem,
+ key,
+ truncateAllocation(chainsAndAllocs[0][1])
+ )
+ )
+ }
+ }
+ })
+
+ // for each evm address, sum up the allocs and add to ecosystemAddresses
+ evmBreakdownAddresses.forEach((value, key) => {
+ const totalAmount = value.reduce((acc, row) => {
+ return acc.add(truncateAllocation(row[1]))
+ }, new BN(0))
+ claimInfos.push(new ClaimInfo('evm', key, totalAmount))
+ })
+
+ // convert into breakdown rows
+ const evmBreakdownRows: EvmBreakdownRow[] = []
+ evmBreakdownAddresses.forEach((chainsAndAllocs, identity) => {
+ chainsAndAllocs.forEach(([chain, alloc]) => {
+ evmBreakdownRows.push({
+ chain,
+ identity,
+ amount: truncateAllocation(alloc),
+ })
+ })
+ })
+
+ // need solana breakdown between nft & defi
+ const nftClaims = parseNftCsv()
+
+ nftClaims.forEach((row) => {
+ if (solanaBreakdownData.has(row.address)) {
+ solanaBreakdownData.get(row.address)?.push({
+ source: 'nft',
+ identity: row.address,
+ amount: truncateAllocation(row.alloc),
+ })
+ } else {
+ solanaBreakdownData.set(row.address, [
+ {
+ source: 'nft',
+ identity: row.address,
+ amount: truncateAllocation(row.alloc),
+ },
+ ])
+ }
+ })
+
+ // sum up all the solana breakdowns for each identity and add to ecosystemAddresses
+ solanaBreakdownData.forEach((value, key) => {
+ const totalAmount = value.reduce((acc, row) => {
+ return acc.add(row.amount)
+ }, new BN(0))
+ claimInfos.push(new ClaimInfo('solana', key, totalAmount))
+ })
+
+ // flatten into breakdown rows
+ const solanaBreakdownRows: SolanaBreakdownRow[] = []
+ solanaBreakdownData.forEach((breakdowns, identity) => {
+ breakdowns.forEach((breakdown) => {
+ solanaBreakdownRows.push(breakdown)
+ })
+ })
+
+ // read all discord claims and add to ecosystemAddresses
+ const discordClaims = parseDiscordClaims()
+ discordClaims.forEach((row) => {
+ claimInfos.push(
+ new ClaimInfo('discord', row.address, truncateAllocation(row.alloc))
+ )
+ })
+
+ return {
+ claimInfos,
+ evmBreakdownRows,
+ solanaBreakdownRows,
+ }
+}
+
+function hasColumns(
+ csvClaims: Papa.ParseResult,
+ columns: string[]
+): void {
+ columns.forEach((column) => {
+ assert(
+ csvClaims.meta.fields?.includes(column),
+ `CSV file does not have required '${column}' column`
+ )
+ })
+}
+
+function parseDefiCsv(defi_csv: string) {
+ const defiCsvClaims = Papa.parse(
+ fs.readFileSync(path.resolve(CSV_DIR, defi_csv), 'utf-8'),
+ {
+ header: true,
+ }
+ )
+
+ hasColumns(defiCsvClaims, ['address', 'chain', 'alloc'])
+
+ const claimsData = defiCsvClaims.data as {
+ address: string
+ chain: string
+ alloc: string
+ }[]
+
+ // group by address
+ // only evm addresses should have multiple values
+ return claimsData.reduce((acc, row) => {
+ const curValues = acc.get(row.address)
+ if (curValues) {
+ acc.set(row.address, [...curValues, [row.chain, row.alloc]])
+ } else {
+ acc.set(row.address, [[row.chain, row.alloc]])
+ }
+ return acc
+ }, new Map())
+}
+
+function parseNftCsv() {
+ const nftCsvClaims = Papa.parse(
+ fs.readFileSync(path.resolve(CSV_DIR, NFT_CLAIMS), 'utf-8'),
+ {
+ header: true,
+ }
+ )
+ hasColumns(nftCsvClaims, ['address', 'alloc'])
+
+ const nftClaims = nftCsvClaims.data as {
+ address: string
+ alloc: string
+ }[]
+ return nftClaims
+}
+
+function parseDiscordClaims(): { address: string; alloc: string }[] {
+ const discordCsvClaims = Papa.parse(
+ fs.readFileSync(path.resolve(CSV_DIR, DISCORD_CLAIMS), 'utf-8'),
+ {
+ header: true,
+ }
+ )
+ hasColumns(discordCsvClaims, ['address', 'alloc'])
+
+ const discordClaims = discordCsvClaims.data as {
+ address: string
+ alloc: string
+ }[]
+
+ const discordClaimsAddrSet = new Set(discordClaims.map((row) => row.address))
+ assert(
+ discordClaims.length === discordClaimsAddrSet.size,
+ 'Discord claims has duplicate addresses'
+ )
+
+ const discordDevCsvClaims = Papa.parse(
+ fs.readFileSync(path.resolve(CSV_DIR, DISCORD_DEV_CLAIMS), 'utf-8'),
+ {
+ header: true,
+ }
+ )
+
+ hasColumns(discordDevCsvClaims, ['address', 'alloc'])
+
+ // filter out addresses that are already in discordClaims
+ const discordDevClaims = (
+ discordDevCsvClaims.data as {
+ address: string
+ alloc: string
+ }[]
+ ).filter((row) => {
+ const isUniqueDevAddress = !discordClaimsAddrSet.has(row.address)
+ if (!isUniqueDevAddress) {
+ console.log(
+ `skipping discord dev claim for ${row.address} because it is already in discord.csv`
+ )
+ }
+ return isUniqueDevAddress
+ })
+
+ return discordClaims.concat(discordDevClaims).map((addrAndAlloc) => {
+ const hashedDiscordId = hashDiscordUserId(
+ DISCORD_HASH_SALT,
+ addrAndAlloc.address
+ )
+ return {
+ address: hashedDiscordId,
+ alloc: addrAndAlloc.alloc,
+ }
+ })
+}
+
+function truncateAllocation(allocation: string): BN {
+ if (allocation.indexOf('.') === -1) {
+ return new BN(allocation + '000000')
+ }
+ const allocationParts = allocation.split('.')
+ assert(allocationParts.length === 2)
+ const allocationInt = allocationParts[0]
+ const allocationNormalized = allocationInt + '000000'
+ const allocationBn = new BN(allocationNormalized)
+ return allocationBn
+}
+
+function getMaxUserAndAmount(claimInfos: ClaimInfo[]): [string, BN] {
+ let maxUser = ''
+ const maxAmount = claimInfos.reduce((prev, curr) => {
+ if (curr.amount.gt(prev)) {
+ maxUser = curr.identity
+ }
+ return BN.max(prev, curr.amount)
+ }, new BN(0))
+ return [maxUser, maxAmount]
+}
+
+function getTotalByEcosystems(claimInfos: ClaimInfo[]): Map {
+ const ecosystemMap = new Map()
+ claimInfos.forEach((claimInfo) => {
+ if (ecosystemMap.has(claimInfo.ecosystem)) {
+ ecosystemMap.set(
+ claimInfo.ecosystem,
+ ecosystemMap.get(claimInfo.ecosystem)?.add(claimInfo.amount) as BN
+ )
+ } else {
+ ecosystemMap.set(claimInfo.ecosystem, claimInfo.amount)
+ }
+ })
+ return ecosystemMap
+}
+
+// Requirements for this script :
+// - Airdrop allocation repo has been downloaded and path to repo set in .env
+// - DB has been migrated
+
+// Extra steps after running this script :
+// - Make sure the tokens are in the treasury account
+// - Make sure the treasury account has the config account as its delegate
+
+async function main() {
+ const mainStart = Date.now()
+ await clearDatabase(pool)
+ const parseCsvStart = Date.now()
+ const { claimInfos, evmBreakdownRows, solanaBreakdownRows } = parseCsvs()
+ const parseCsvEnd = Date.now()
+ if (DEBUG) {
+ const [maxUser, maxAmount] = getMaxUserAndAmount(claimInfos)
+ console.log(`maxUser: ${maxUser} maxAmount: ${maxAmount.toString()}`)
+
+ Ecosystems.forEach((ecosystem) => {
+ const [maxEcoUser, maxEcoAmount] = getMaxUserAndAmount(
+ claimInfos.filter((claimInfo) => claimInfo.ecosystem === ecosystem)
+ )
+ const ecoAmounts = claimInfos
+ .filter((claimInfo) => claimInfo.ecosystem === ecosystem)
+ .reduce((acc, curr) => {
+ const amountCount = acc.get(curr.amount.toNumber()) ?? 0
+ acc.set(curr.amount.toNumber(), amountCount + 1)
+ return acc
+ }, new Map()) //map
+ const ecoAmountsArr = Array.from(ecoAmounts.entries())
+ ecoAmountsArr.sort((a, b) => {
+ return b[0] - a[0]
+ })
+
+ console.log(
+ `ecosystem: ${ecosystem} maxEcoUser: ${maxEcoUser} maxEcoAmount: ${maxEcoAmount
+ .div(new BN(1000000))
+ .toString()}
+ ecoAmountsArr: ${JSON.stringify(ecoAmountsArr)}
+ `
+ )
+ })
+ const ecosystemMap = getTotalByEcosystems(claimInfos)
+ let totalAirdrop = new BN(0)
+ ecosystemMap.forEach((amount, ecosystem) => {
+ totalAirdrop = totalAirdrop.add(amount)
+ })
+ ecosystemMap.forEach((amount, ecosystem) => {
+ console.log(
+ `ecosystem: ${ecosystem} amount: ${amount
+ .div(new BN(1000000))
+ .toString()} - ${amount
+ .mul(new BN(100))
+ .div(totalAirdrop)
+ .toString()}% of total airdrop`
+ )
+ })
+ assert(
+ evmBreakdownRows.every((row) =>
+ EVM_CHAINS.includes(row.chain as EvmChains)
+ )
+ )
+ }
+ const maxAmount = getMaxAmount(claimInfos)
+
+ checkClaimsMatchEvmBreakdown(claimInfos, evmBreakdownRows)
+
+ checkClaimsMatchSolanaBreakdown(claimInfos, solanaBreakdownRows)
+
+ // sort by amount & identity
+ claimInfos.sort((a, b) => {
+ const amountCmp = b.amount.cmp(a.amount)
+ return amountCmp != 0 ? amountCmp : a.identity.localeCompare(b.identity)
+ })
+
+ // Add data to database
+ const addClaimInfosStart = Date.now()
+ const root = await addClaimInfosToDatabase(pool, claimInfos)
+ console.log('THE ROOT IS :', root.toString('hex'))
+ const addClaimInfoEnd = Date.now()
+ console.log(
+ `\n\nadded claim infos to database time: ${
+ addClaimInfoEnd - addClaimInfosStart
+ } ms`
+ )
+ const addEvmStart = Date.now()
+ await addEvmBreakdownsToDatabase(pool, evmBreakdownRows)
+ const addEvmEnd = Date.now()
+ console.log(`added evm breakdowns time : ${addEvmEnd - addEvmStart} ms`)
+ const addSolStart = Date.now()
+ await addSolanaBreakdownsToDatabase(pool, solanaBreakdownRows)
+ const addSolEnd = Date.now()
+ console.log(
+ `added solana breakdowns to db time: ${addSolEnd - addSolStart} ms`
+ )
+
+ console.log(`
+ \n\n
+ parseCsvTime: ${parseCsvEnd - parseCsvStart}
+ addClaimInfoTime: ${addClaimInfoEnd - addClaimInfosStart}
+ addEvmTime: ${addEvmEnd - addEvmStart}
+ addSolTime: ${addSolEnd - addSolStart}
+ \n\n`)
+
+ // Initialize the token dispenser
+ const tokenDispenserProvider = new TokenDispenserProvider(
+ ENDPOINT,
+ new NodeWallet(DEPLOYER_WALLET),
+ // for local testing
+ // loadFunderWallet(),
+ new PublicKey(PROGRAM_ID),
+ {
+ skipPreflight: true,
+ preflightCommitment: 'processed',
+ commitment: 'processed',
+ }
+ )
+
+ await tokenDispenserProvider.initialize(
+ root,
+ PYTH_MINT,
+ PYTH_TREASURY,
+ DISPENSER_GUARD.publicKey,
+ FUNDER_KEYPAIR.publicKey,
+ maxAmount
+ )
+
+ // for local testing
+ // const mintAndTreasury = await tokenDispenserProvider.setupMintAndTreasury()
+ // await tokenDispenserProvider.initialize(
+ // root,
+ // mintAndTreasury.mint.publicKey,
+ // mintAndTreasury.treasury,
+ // DISPENSER_GUARD.publicKey,
+ // FUNDER_KEYPAIR.publicKey,
+ // maxAmount
+ // )
+ const mainEnd = Date.now()
+ console.log(`\n\ninitialized token dispenser\n\n`)
+
+ console.log(`
+ \n\n
+ totalTime: ${mainEnd - mainStart}
+ parseCsvTime: ${parseCsvEnd - parseCsvStart}
+ addClaimInfoTime: ${addClaimInfoEnd - addClaimInfosStart}
+ addEvmTime: ${addEvmEnd - addEvmStart}
+ addSolTime: ${addSolEnd - addSolStart}
+ \n\n`)
+}
+
+;(async () => {
+ try {
+ await main()
+ } catch (e) {
+ console.error(`error from populate_from_csv: ${e}`)
+ process.exit(1)
+ }
+})()
diff --git a/frontend/utils/chain-registry.ts b/frontend/utils/chain-registry.ts
index d211cfc7..8348ac79 100644
--- a/frontend/utils/chain-registry.ts
+++ b/frontend/utils/chain-registry.ts
@@ -1,29 +1,42 @@
export const assets = [
{
$schema: '../assetlist.schema.json',
- chain_name: 'neutron',
+ chain_name: 'terra',
assets: [
{
- description: 'The native token of Neutron chain.',
+ description: 'The native staking token of Terra Classic.',
denom_units: [
{
- denom: 'untrn',
+ denom: 'uluna',
exponent: 0,
+ aliases: ['microluna'],
},
{
- denom: 'ntrn',
+ denom: 'mluna',
+ exponent: 3,
+ aliases: ['milliluna'],
+ },
+ {
+ denom: 'luna',
exponent: 6,
+ aliases: ['lunc'],
},
],
- base: 'untrn',
- name: 'Neutron',
- display: 'ntrn',
- symbol: 'NTRN',
+ base: 'uluna',
+ name: 'Luna Classic',
+ display: 'luna',
+ symbol: 'LUNC',
logo_URIs: {
- png: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/neutron/images/ntrn.png',
- svg: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/neutron/images/ntrn.svg',
+ png: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/luna.png',
+ svg: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/luna.svg',
},
- coingecko_id: 'neutron',
+ coingecko_id: 'terra-luna',
+ images: [
+ {
+ png: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/luna.png',
+ svg: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/luna.svg',
+ },
+ ],
},
],
},
@@ -56,467 +69,368 @@ export const assets = [
},
],
},
- {
- $schema: '../assetlist.schema.json',
- chain_name: 'sei',
- assets: [
- {
- description: 'The native staking token of Sei.',
- denom_units: [
- {
- denom: 'usei',
- exponent: 0,
- },
- {
- denom: 'sei',
- exponent: 6,
- },
- ],
- base: 'usei',
- name: 'Sei',
- display: 'sei',
- symbol: 'SEI',
- logo_URIs: {
- png: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/sei/images/sei.png',
- svg: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/sei/images/sei.svg',
- },
- coingecko_id: 'sei-network',
- },
- {
- description:
- 'OIN Token ($OIN) is a groundbreaking digital asset developed on the $SEI Blockchain. It transcends being merely a cryptocurrency; $OIN stands as a robust store of value, symbolizing the future of decentralized finance and its potential to reshape the crypto landscape.',
- denom_units: [
- {
- denom: 'factory/sei12q0zv3c4cd9jkupn0krazdycc5ftw9wzt9vmhu/OIN',
- exponent: 0,
- },
- {
- denom: 'oin',
- exponent: 6,
- },
- ],
- address: 'sei1thgp6wamxwqt7rthfkeehktmq0ujh5kspluw6w',
- base: 'factory/sei1thgp6wamxwqt7rthfkeehktmq0ujh5kspluw6w/OIN',
- name: 'OIN STORE OF VALUE',
- display: 'oin',
- symbol: 'OIN',
- logo_URIs: {
- png: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/sei/images/oin.png',
- },
- coingecko_id: '',
- },
- ],
- },
]
export const chains = [
{
$schema: '../chain.schema.json',
- chain_name: 'sei',
+ chain_name: 'terra',
status: 'live',
- website: 'https://www.sei.io/',
network_type: 'mainnet',
- pretty_name: 'Sei',
- chain_id: 'pacific-1',
- bech32_prefix: 'sei',
- daemon_name: 'seid',
- node_home: '$HOME/.sei',
- key_algos: ['secp256k1'],
- slip44: 118,
+ pretty_name: 'Terra Classic',
+ chain_id: 'columbus-5',
+ daemon_name: 'terrad',
+ node_home: '$HOME/.terra',
+ bech32_prefix: 'terra',
+ slip44: 330,
fees: {
fee_tokens: [
{
- denom: 'usei',
- fixed_min_gas_price: 0.1,
- low_gas_price: 0.1,
- average_gas_price: 0.1,
- high_gas_price: 0.25,
+ denom: 'uluna',
+ low_gas_price: 28.325,
+ average_gas_price: 28.325,
+ high_gas_price: 50,
},
- ],
- },
- staking: {
- staking_tokens: [
{
- denom: 'usei',
+ denom: 'usdr',
+ low_gas_price: 0.52469,
+ average_gas_price: 0.52469,
+ high_gas_price: 0.52469,
},
- ],
- },
- codebase: {
- git_repo: 'https://github.com/sei-protocol/sei-chain',
- recommended_version: 'v3.0.9',
- compatible_versions: ['v3.0.9'],
- ibc_go_version: 'v3.1.0',
- cosmos_sdk_version: 'v0.45.10',
- cosmwasm_version: 'v0.27.0',
- cosmwasm_enabled: true,
- cosmwasm_path: '$HOME/.sei/wasm',
- genesis: {
- genesis_url:
- 'https://raw.githubusercontent.com/sei-protocol/testnet/main/pacific-1/genesis.json',
- },
- versions: [
{
- name: '',
- recommended_version: '3.0.8',
- compatible_versions: ['3.0.8'],
- ibc_go_version: 'v3.0.0',
- cosmos_sdk_version: 'v0.45.10',
- cosmwasm_version: 'v0.27.0',
- cosmwasm_enabled: true,
- cosmwasm_path: '$HOME/.sei/wasm',
- next_version_name: 'v3.0.9',
- },
- {
- name: 'v3.0.9',
- recommended_version: 'v3.0.9',
- compatible_versions: ['v3.0.9'],
- proposal: 24,
- height: 25259000,
- ibc_go_version: 'v3.1.0',
- cosmos_sdk_version: 'v0.45.10',
- cosmwasm_version: 'v0.27.0',
- cosmwasm_enabled: true,
- cosmwasm_path: '$HOME/.sei/wasm',
- next_version_name: '',
+ denom: 'uusd',
+ low_gas_price: 0.75,
+ average_gas_price: 0.75,
+ high_gas_price: 0.75,
},
- ],
- },
- logo_URIs: {
- png: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/sei/images/sei.png',
- svg: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/sei/images/sei.svg',
- },
- peers: {
- seeds: [
{
- id: '20e1000e88125698264454a884812746c2eb4807',
- address: 'seeds.lavenderfive.com:11956',
- provider: 'Lavender.Five Nodes 🐝',
+ denom: 'ukrw',
+ low_gas_price: 850,
+ average_gas_price: 850,
+ high_gas_price: 850,
},
{
- id: '7cbcea0b3041960d1d7b8a6a2eccce0e1f7add50',
- address: 'seeds.whispernode.com:11956',
- provider: 'WhisperNode 🤐',
+ denom: 'umnt',
+ low_gas_price: 2142.855,
+ average_gas_price: 2142.855,
+ high_gas_price: 2142.855,
},
- ],
- persistent_peers: [
{
- id: '20e1000e88125698264454a884812746c2eb4807',
- address: 'seeds.lavenderfive.com:11956',
- provider: 'Lavender.Five Nodes 🐝',
+ denom: 'ueur',
+ low_gas_price: 0.625,
+ average_gas_price: 0.625,
+ high_gas_price: 0.625,
},
- ],
- },
- apis: {
- rpc: [
{
- address: 'https://sei-rpc.lavenderfive.com:443',
- provider: 'Lavender.Five Nodes 🐝',
+ denom: 'ucny',
+ low_gas_price: 4.9,
+ average_gas_price: 4.9,
+ high_gas_price: 4.9,
},
{
- address: 'https://sei-rpc.polkachu.com/',
- provider: 'polkachu.com',
+ denom: 'ujpy',
+ low_gas_price: 81.85,
+ average_gas_price: 81.85,
+ high_gas_price: 81.85,
},
{
- address: 'https://sei-rpc.brocha.in/',
- provider: 'Brochain',
+ denom: 'ugbp',
+ low_gas_price: 0.55,
+ average_gas_price: 0.55,
+ high_gas_price: 0.55,
},
{
- address: 'https://rpc-sei.stingray.plus/',
- provider: 'StingRay',
+ denom: 'uinr',
+ low_gas_price: 54.4,
+ average_gas_price: 54.4,
+ high_gas_price: 54.4,
},
{
- address: 'https://rpc-sei.rhinostake.com',
- provider: 'RHINO',
+ denom: 'ucad',
+ low_gas_price: 0.95,
+ average_gas_price: 0.95,
+ high_gas_price: 0.95,
},
{
- address: 'https://rpc-sei.whispernode.com:443',
- provider: 'WhisperNode 🤐',
+ denom: 'uchf',
+ low_gas_price: 0.7,
+ average_gas_price: 0.7,
+ high_gas_price: 0.7,
},
- ],
- rest: [
{
- address: 'https://sei-api.lavenderfive.com:443',
- provider: 'Lavender.Five Nodes 🐝',
+ denom: 'uaud',
+ low_gas_price: 0.95,
+ average_gas_price: 0.95,
+ high_gas_price: 0.95,
},
{
- address: 'https://sei-api.polkachu.com/',
- provider: 'polkachu.com',
+ denom: 'usgd',
+ low_gas_price: 1,
+ average_gas_price: 1,
+ high_gas_price: 1,
},
{
- address: 'https://sei-rest.brocha.in/',
- provider: 'Brochain',
+ denom: 'uthb',
+ low_gas_price: 23.1,
+ average_gas_price: 23.1,
+ high_gas_price: 23.1,
},
{
- address: 'https://api-sei.stingray.plus/',
- provider: 'StingRay',
+ denom: 'usek',
+ low_gas_price: 6.25,
+ average_gas_price: 6.25,
+ high_gas_price: 6.25,
},
{
- address: 'https://rest-sei.rhinostake.com',
- provider: 'RHINO',
+ denom: 'unok',
+ low_gas_price: 6.25,
+ average_gas_price: 6.25,
+ high_gas_price: 6.25,
},
{
- address: 'https://lcd-sei.whispernode.com:443',
- provider: 'WhisperNode 🤐',
+ denom: 'udkk',
+ low_gas_price: 4.5,
+ average_gas_price: 4.5,
+ high_gas_price: 4.5,
},
- ],
- grpc: [
{
- address: 'https://sei-grpc.lavenderfive.com:443',
- provider: 'Lavender.Five Nodes 🐝',
+ denom: 'uidr',
+ low_gas_price: 10900,
+ average_gas_price: 10900,
+ high_gas_price: 10900,
},
{
- address: 'https://sei-grpc.polkachu.com:11990/',
- provider: 'polkachu.com',
+ denom: 'uphp',
+ low_gas_price: 38,
+ average_gas_price: 38,
+ high_gas_price: 38,
},
{
- address: 'https://grpc-sei.whispernode.com:443',
- provider: 'WhisperNode 🤐',
+ denom: 'uhkd',
+ low_gas_price: 5.85,
+ average_gas_price: 5.85,
+ high_gas_price: 5.85,
},
- ],
- },
- explorers: [
- {
- kind: 'ping.pub',
- url: 'https://ping.pub/sei',
- tx_page: 'https://ping.pub/sei/tx/${txHash}',
- account_page: 'https://ping.pub/sei/account/${accountAddress}',
- },
- {
- kind: 'mintscan',
- url: 'https://www.mintscan.io/sei',
- tx_page: 'https://www.mintscan.io/sei/transactions/${txHash}',
- account_page: 'https://www.mintscan.io/sei/accounts/${accountAddress}',
- },
- {
- kind: 'seiscan',
- url: 'https://www.seiscan.app/pacific-1',
- tx_page: 'https://www.seiscan.app/pacific-1/txs/${txHash}',
- account_page:
- 'https://www.seiscan.app/pacific-1/accounts/${accountAddress}',
- },
- ],
- },
- {
- $schema: '../chain.schema.json',
- chain_name: 'neutron',
- status: 'live',
- network_type: 'mainnet',
- pretty_name: 'Neutron',
- chain_id: 'neutron-1',
- bech32_prefix: 'neutron',
- daemon_name: 'neutrond',
- node_home: '$HOME/.neutrond',
- key_algos: ['secp256k1'],
- slip44: 118,
- fees: {
- fee_tokens: [
{
- denom: 'untrn',
- low_gas_price: 0.01,
- average_gas_price: 0.025,
- high_gas_price: 0.05,
+ denom: 'umyr',
+ low_gas_price: 3,
+ average_gas_price: 3,
+ high_gas_price: 3,
+ },
+ {
+ denom: 'utwd',
+ low_gas_price: 20,
+ average_gas_price: 20,
+ high_gas_price: 20,
},
],
},
staking: {
staking_tokens: [
{
- denom: 'untrn',
+ denom: 'uluna',
},
],
},
codebase: {
- git_repo: 'https://github.com/neutron-org/neutron',
- recommended_version: 'v1.0.4',
- compatible_versions: ['v1.0.3', 'v1.0.4'],
- cosmos_sdk_version: '0.45',
- consensus: {
- type: 'tendermint',
- version: '0.34',
- },
- cosmwasm_version: '0.31',
- cosmwasm_enabled: true,
- ibc_go_version: '4.3.0',
+ git_repo: 'https://github.com/classic-terra/core',
+ recommended_version: 'v2.1.1',
+ compatible_versions: ['v2.1.1'],
genesis: {
- genesis_url:
- 'https://raw.githubusercontent.com/neutron-org/mainnet-assets/main/neutron-1-genesis.json',
+ name: '1.0.5',
+ genesis_url: 'https://tfl-columbus-5.s3.amazonaws.com/genesis.json',
},
versions: [
{
- name: 'v1.0.1',
- recommended_version: 'v1.0.4',
- compatible_versions: ['v1.0.3', 'v1.0.4'],
- cosmos_sdk_version: '0.45',
+ name: '1.0.5',
+ tag: 'v1.0.5-full-archive',
+ height: 0,
+ next_version_name: '1.1.0',
+ binaries: {
+ 'linux/amd64':
+ 'https://github.com/terra-money/classic-core/releases/download/v1.0.5-full-archive/terra_1.0.5_Linux_x86_64.tar.gz?checksum=sha256:af3ee3bd99bd719d6d9a93a40af9f0bc49bb3866c68e923e284876784126f38c',
+ },
+ },
+ {
+ name: '1.1.0',
+ tag: 'v1.1.0',
+ height: 11734000,
+ recommended_version: 'v1.1.0',
+ compatible_versions: ['v1.1.0'],
+ next_version_name: '2.0.1',
+ binaries: {
+ 'linux/amd64':
+ 'https://github.com/terra-money/classic-core/releases/download/v1.1.0/terra_1.1.0_Linux_x86_64.tar.gz?checksum=sha256:fd83c14bcfadea36ad444c219ab557b9d65d2f74be0684498a5c41e3df7cb535',
+ },
+ },
+ {
+ name: '2.0.1',
+ tag: 'v2.0.1',
+ height: 12815210,
+ cosmos_sdk_version: '0.45.13',
+ cosmwasm_enabled: true,
+ cosmwasm_version: '0.16.7',
+ ibc_go_version: '1.3.1',
consensus: {
type: 'tendermint',
- version: '0.34',
+ version: '0.34.24',
},
- cosmwasm_version: '0.31',
+ binaries: {
+ 'linux/amd64':
+ 'https://github.com/terra-money/classic-core/releases/download/v2.0.1/terra_2.0.1_Linux_x86_64.tar.gz?checksum=sha256:b9edfd51080c9c9ae16b30afd1b8490d7278e51d521ccc0f2afcbb7e3b389b8d',
+ },
+ },
+ {
+ name: '2.1.1',
+ tag: 'v2.1.1',
+ height: 13215800,
+ cosmos_sdk_version: '0.45.14',
cosmwasm_enabled: true,
- ibc_go_version: '4.3.0',
+ cosmwasm_version: '0.30.0',
+ ibc_go_version: '4.3.1',
+ consensus: {
+ type: 'tendermint',
+ version: '0.34.24',
+ },
+ binaries: {
+ 'linux/amd64':
+ 'https://github.com/terra-money/classic-core/releases/download/v2.1.1/terra_2.1.1_Linux_x86_64.tar.gz?checksum=sha256:9bf91be244af95f1afcf7fc1ddb1852aa96651adf94e9668c16c7df5596100d6',
+ },
},
],
},
logo_URIs: {
- png: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/neutron/images/neutron-black-logo.png',
- svg: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/neutron/images/neutron-black-logo.svg',
+ png: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/luna.png',
+ svg: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/luna.svg',
},
peers: {
seeds: [
{
- id: '24f609fb5946ca3a979f40b7f54132c00104433e',
- address: 'p2p-erheim.neutron-1.neutron.org:26656',
- provider: 'Neutron',
+ id: 'ebc272824924ea1a27ea3183dd0b9ba713494f83',
+ address: 'terraclassic-mainnet-seed.autostake.com:26676',
+ provider: 'AutoStake 🛡️ Slash Protected',
},
{
- id: 'b1c6fa570a184c56d0d736d260b8065d887e717c',
- address: 'p2p-kralum.neutron-1.neutron.org:26656',
- provider: 'Neutron',
+ id: 'b1bdf6249fb58b4c8284aff8a9c5b2804d822261',
+ address: 'seed.terra.synergynodes.com:26656',
+ provider: 'www.synergynodes.com',
},
{
- id: '20e1000e88125698264454a884812746c2eb4807',
- address: 'seeds.lavenderfive.com:19156',
- provider: 'Lavender.Five Nodes 🐝',
+ id: '65d86ab6024153286b823a3950e9055478effb04',
+ address: 'terra.inotel.ro:26656',
+ provider: 'www.inotel.ro',
},
{
- id: 'f4422e68f9a678838522d75fa8221985c723294d',
- address: 'seeds.whispernode.com:19156',
- provider: 'WhisperNode🤐',
+ id: '8542cd7e6bf9d260fef543bc49e59be5a3fa9074',
+ address: 'seed.publicnode.com:26656',
+ provider: 'Allnodes ⚡️ Nodes & Staking',
},
+ ],
+ persistent_peers: [
{
- id: 'e1b058e5cfa2b836ddaa496b10911da62dcf182e',
- address: 'neutron-seed-de.allnodes.me:26656',
- provider: 'Allnodes.com ⚡️ Nodes & Staking',
+ id: 'ebc272824924ea1a27ea3183dd0b9ba713494f83',
+ address: 'terraclassic-mainnet-peer.autostake.com:26676',
+ provider: 'AutoStake 🛡️ Slash Protected',
},
{
- id: 'e726816f42831689eab9378d5d577f1d06d25716',
- address: 'neutron-seed-us.allnodes.me:26656',
- provider: 'Allnodes.com ⚡️ Nodes & Staking',
+ id: 'b1bdf6249fb58b4c8284aff8a9c5b2804d822261',
+ address: 'seed.terra.synergynodes.com:26656',
+ provider: 'www.synergynodes.com',
},
- ],
- persistent_peers: [
{
- id: 'e5d2743d9a3de514e4f7b9461bf3f0c1500c58d9',
- address: 'neutron.peer.stakewith.us:39956',
- provider: 'StakeWithUs',
+ id: '65d86ab6024153286b823a3950e9055478effb04',
+ address: 'terra.inotel.ro:26656',
+ provider: 'www.inotel.ro',
},
],
},
apis: {
rpc: [
{
- address: 'https://rpc-kralum.neutron-1.neutron.org',
- provider: 'Neutron',
- },
- {
- address: 'https://rpc.novel.remedy.tm.p2p.org',
- provider: 'P2P',
- },
- {
- address: 'https://neutron-rpc.lavenderfive.com',
- provider: 'Lavender.Five Nodes 🐝',
- },
- {
- address: 'https://rpc-neutron.whispernode.com',
- provider: 'WhisperNode🤐',
+ address: 'https://terra-classic-rpc.publicnode.com:443',
+ provider: 'Allnodes ⚡️ Nodes & Staking',
},
{
- address: 'https://rpc-neutron.cosmos-spaces.cloud',
- provider: 'Cosmos Spaces',
- },
- {
- address: 'http://posthuman-neutron-rpc.ingress.europlots.com',
- provider: 'POSTHUMAN ꝏ DVS',
- },
- {
- address: 'http://rpc.neutron.nodestake.top',
- provider: 'NodeStake',
+ address: 'https://rpc-terra-ia.cosmosia.notional.ventures/',
+ provider: 'Notional',
},
{
- address: 'https://neutron-rpc.publicnode.com',
- provider: 'Allnodes.com ⚡️ Nodes & Staking',
+ address: 'https://terraclassic-mainnet-rpc.autostake.com:443',
+ provider: 'AutoStake 🛡️ Slash Protected',
},
{
- address: 'https://community.nuxian-node.ch:6797/neutron/trpc',
- provider: 'PRO Delegators',
+ address: 'https://terraclassic-rpc-server-01.stakely.io',
+ provider: 'Stakely',
},
],
rest: [
{
- address: 'https://rest-kralum.neutron-1.neutron.org',
- provider: 'Neutron',
- },
- {
- address: 'https://api.novel.remedy.tm.p2p.org',
- provider: 'P2P',
- },
- {
- address: 'https://neutron-api.lavenderfive.com',
- provider: 'Lavender.Five Nodes 🐝',
- },
- {
- address: 'https://lcd-neutron.whispernode.com',
- provider: 'WhisperNode🤐',
- },
- {
- address: 'https://api-neutron.cosmos-spaces.cloud',
- provider: 'Cosmos Spaces',
+ address: 'https://terra-classic-lcd.publicnode.com',
+ provider: 'Allnodes ⚡️ Nodes & Staking',
},
{
- address: 'http://api.neutron.nodestake.top',
- provider: 'NodeStake',
+ address: 'https://api-terra-ia.cosmosia.notional.ventures/',
+ provider: 'Notional',
},
{
- address: 'https://neutron-rest.publicnode.com',
- provider: 'Allnodes.com ⚡️ Nodes & Staking',
+ address: 'https://terraclassic-mainnet-lcd.autostake.com:443',
+ provider: 'AutoStake 🛡️ Slash Protected',
},
{
- address: 'https://community.nuxian-node.ch:6797/neutron/crpc',
- provider: 'PRO Delegators',
+ address: 'https://terraclassic-lcd-server-01.stakely.io',
+ provider: 'Stakely',
},
],
grpc: [
{
- address: 'grpc-kralum.neutron-1.neutron.org:80',
- provider: 'Neutron',
- },
- {
- address: 'https://grpc.novel.remedy.tm.p2p.org',
- provider: 'P2P',
+ address: 'grpc.terrarebels.net',
+ provider: 'Terra Rebels',
},
{
- address: 'https://grpc-web.novel.remedy.tm.p2p.org',
- provider: 'P2P',
+ address: 'terra-classic-grpc.publicnode.com:443',
+ provider: 'Allnodes ⚡️ Nodes & Staking',
},
{
- address: 'neutron-grpc.lavenderfive.com:443',
- provider: 'Lavender.Five Nodes 🐝',
- },
- {
- address: 'grpc-neutron.whispernode.com:443',
- provider: 'WhisperNode🤐',
- },
- {
- address: 'grpc-neutron.cosmos-spaces.cloud:3090',
- provider: 'Cosmos Spaces',
- },
- {
- address: 'grpc.neutron.nodestake.top:9090',
- provider: 'NodeStake',
+ address: 'grpc-terra-ia.cosmosia.notional.ventures:443',
+ provider: 'Notional',
},
{
- address: 'neutron-grpc.publicnode.com:443',
- provider: 'Allnodes.com ⚡️ Nodes & Staking',
+ address: 'terraclassic-mainnet-grpc.autostake.com:443',
+ provider: 'AutoStake 🛡️ Slash Protected',
},
],
},
explorers: [
{
- kind: 'Mintscan',
- url: 'https://www.mintscan.io/neutron',
- tx_page: 'https://www.mintscan.io/neutron/transactions/${txHash}',
+ kind: 'ping.pub',
+ url: 'https://ping.pub/terra-luna',
+ tx_page: 'https://ping.pub/terra-luna/tx/${txHash}',
+ },
+ {
+ kind: 'atomscan',
+ url: 'https://atomscan.com/terra',
+ tx_page: 'https://atomscan.com/terra/transactions/${txHash}',
+ account_page: 'https://atomscan.com/terra/accounts/${accountAddress}',
+ },
+ {
+ kind: 'finder',
+ url: 'https://finder.terra.money/classic',
+ tx_page: 'https://finder.terra.money/classic/tx/${txHash}',
+ account_page:
+ 'https://finder.terra.money/classic/address/${accountAddress}',
+ },
+ {
+ kind: 'finder',
+ url: 'https://finder.terrarebels.net/classic',
+ tx_page: 'https://finder.terrarebels.net/classic/tx/${txHash}',
account_page:
- 'https://www.mintscan.io/neutron/accounts/${accountAddress}',
+ 'https://finder.terrarebels.net/classic/address/${accountAddress}',
+ },
+ ],
+ images: [
+ {
+ png: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/luna.png',
+ svg: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/luna.svg',
},
],
},
diff --git a/frontend/utils/constants.ts b/frontend/utils/constants.ts
index 2f8228f4..51c404f0 100644
--- a/frontend/utils/constants.ts
+++ b/frontend/utils/constants.ts
@@ -24,4 +24,4 @@ export const EVM_CHAINS = [
'wemix-mainnet',
] as const
-export type EvmChains = (typeof EVM_CHAINS)[number]
+export type EvmChains = typeof EVM_CHAINS[number]
diff --git a/frontend/utils/db.ts b/frontend/utils/db.ts
new file mode 100644
index 00000000..245ecfd5
--- /dev/null
+++ b/frontend/utils/db.ts
@@ -0,0 +1,282 @@
+import { Pool } from 'pg'
+import dotenv from 'dotenv'
+import {
+ TestEvmWallet,
+ TestSolanaWallet,
+ TestWallet,
+} from '../claim_sdk/testWallets'
+import { ClaimInfo, Ecosystem, Ecosystems } from '../claim_sdk/claim'
+import { getMaxAmount } from '../claim_sdk/claim'
+import * as anchor from '@coral-xyz/anchor'
+import { MerkleTree } from '../claim_sdk/merkleTree'
+import { BN } from 'bn.js'
+const sql = require('sql') as any
+dotenv.config() // Load environment variables from .env file
+
+const CHUNK_SIZE = 1000
+const SOLANA_ECOSYSTEM_INDEX = 2
+const EVM_ECOSYSTEM_INDEX = 3
+export const EVM_CHAINS = [
+ 'optimism-mainnet',
+ 'arbitrum-mainnet',
+ 'cronos-mainnet',
+ 'zksync-mainnet',
+ 'bsc-mainnet',
+ 'base-mainnet',
+ 'evmos-mainnet',
+ 'mantle-mainnet',
+ 'linea-mainnet',
+ 'polygon-zkevm-mainnet',
+ 'avalanche-mainnet',
+ 'matic-mainnet',
+ 'aurora-mainnet',
+ 'eth-mainnet',
+ 'confluxespace-mainnet',
+ 'celo-mainnet',
+ 'meter-mainnet',
+ 'gnosis-mainnet',
+ 'kcc-mainnet',
+ 'wemix-mainnet',
+] as const
+
+export type SOLANA_SOURCES = 'nft' | 'defi'
+
+export type EvmChains = typeof EVM_CHAINS[number]
+
+export type EvmBreakdownRow = {
+ chain: string
+ identity: string
+ amount: anchor.BN
+}
+
+export type SolanaBreakdownRow = {
+ source: SOLANA_SOURCES
+ identity: string
+ amount: anchor.BN
+}
+
+/** Get the database pool with the default configuration. */
+export function getDatabasePool(): Pool {
+ // NOTE: This uses the PG* environment variables by default to configure the connection.
+ return new Pool()
+}
+
+export async function clearDatabase(pool: Pool) {
+ await pool.query('DELETE FROM claims', [])
+ await pool.query('DELETE FROM evm_breakdowns', [])
+ await pool.query('DELETE FROM solana_breakdowns', [])
+}
+
+export async function addClaimInfosToDatabase(
+ pool: Pool,
+ claimInfos: ClaimInfo[]
+): Promise {
+ console.log('ADDING :', claimInfos.length, ' CLAIM INFOS')
+ const merkleTreeStart = Date.now()
+ const merkleTree = new MerkleTree(
+ claimInfos.map((claimInfo) => {
+ return claimInfo.toBuffer()
+ })
+ )
+ const merkleTreeEnd = Date.now()
+
+ console.log(
+ `\n\nbuilt merkle tree time: ${merkleTreeEnd - merkleTreeStart}\n\n`
+ )
+
+ let claimInfoChunks = []
+ const chunkCounts = [...Array(Math.ceil(claimInfos.length / CHUNK_SIZE))]
+
+ const claimInfoChunksStart = Date.now()
+
+ claimInfoChunks = chunkCounts.map((_, i) => {
+ if (i % 100 === 0) {
+ console.log(`\n\n making claimInfo chunk ${i}/${chunkCounts.length}\n\n`)
+ }
+ let chunk = claimInfos.splice(0, CHUNK_SIZE)
+ return chunk.map((claimInfo) => {
+ return {
+ ecosystem: claimInfo.ecosystem,
+ identity: claimInfo.identity,
+ amount: claimInfo.amount.toString(),
+ proof_of_inclusion: merkleTree.prove(claimInfo.toBuffer()),
+ }
+ })
+ })
+ const claimInfoChunksEnd = Date.now()
+
+ console.log(
+ `\n\nclaiminfoChunks time: ${claimInfoChunksEnd - claimInfoChunksStart}\n\n`
+ )
+
+ let Claims = sql.define({
+ name: 'claims',
+ columns: ['ecosystem', 'identity', 'amount', 'proof_of_inclusion'],
+ })
+ const claimsInsertStart = Date.now()
+ let chunkCount = 0
+ for (const claimInfoChunk of claimInfoChunks) {
+ let query = Claims.insert(claimInfoChunk).toQuery()
+ await pool.query(query)
+ chunkCount++
+ if (chunkCount % 10 === 0) {
+ console.log(
+ `\n\ninserted ${chunkCount}/${claimInfoChunks.length} chunks\n\n`
+ )
+ }
+ }
+ const claimsInsertEnd = Date.now()
+ console.log(
+ `\n\nclaimsInsert time: ${claimsInsertEnd - claimsInsertStart}\n\n`
+ )
+ return merkleTree.root
+}
+
+export async function addTestWalletsToDatabase(
+ pool: Pool,
+ testWallets: Record
+): Promise<[Buffer, anchor.BN]> {
+ const claimInfos: ClaimInfo[] = Ecosystems.map(
+ (ecosystem, ecosystemIndex) => {
+ return testWallets[ecosystem].map((testWallet, index) => {
+ return new ClaimInfo(
+ ecosystem,
+ testWallet.address(),
+ new anchor.BN(1000000 * (ecosystemIndex + 1) + 100000 * index) // The amount of tokens is deterministic based on the order of the test wallets
+ )
+ })
+ }
+ ).flat(1)
+
+ const maxAmount = getMaxAmount(claimInfos)
+
+ return [await addClaimInfosToDatabase(pool, claimInfos), maxAmount]
+}
+
+export async function addEvmBreakdownsToDatabase(
+ pool: Pool,
+ evmBreakdowns: EvmBreakdownRow[]
+) {
+ console.log('INSERTING :', evmBreakdowns.length, ' EVM BREAKDOWNS')
+ const chunks = []
+ while (evmBreakdowns.length) {
+ chunks.push(
+ evmBreakdowns.splice(0, CHUNK_SIZE).map((row) => {
+ return {
+ chain: row.chain,
+ amount: row.amount.toString(),
+ identity: row.identity,
+ }
+ })
+ )
+ }
+
+ const EvmBreakdowns = sql.define({
+ name: 'evm_breakdowns',
+ columns: ['chain', 'identity', 'amount'],
+ })
+
+ for (const chunk of chunks) {
+ const query = EvmBreakdowns.insert(chunk).toQuery()
+ await pool.query(query)
+ }
+}
+
+export async function addTestEvmBreakdown(
+ pool: Pool,
+ testEvmWallets: TestEvmWallet[]
+): Promise {
+ const claimInfos = testEvmWallets.map(
+ (testEvmWallet, index) =>
+ new ClaimInfo(
+ 'evm',
+ testEvmWallet.address(),
+ new anchor.BN(1000000 * EVM_ECOSYSTEM_INDEX + 100000 * index)
+ )
+ )
+ const rows: EvmBreakdownRow[] = []
+ for (let claimInfo of claimInfos) {
+ const shuffled = EVM_CHAINS.map((value) => ({ value, sort: Math.random() }))
+ .sort((a, b) => a.sort - b.sort)
+ .map(({ value }) => value)
+
+ rows.push({
+ chain: shuffled[0],
+ identity: claimInfo.identity,
+ amount: claimInfo.amount.div(new anchor.BN(3)),
+ })
+ rows.push({
+ chain: shuffled[1],
+ identity: claimInfo.identity,
+ amount: claimInfo.amount.div(new anchor.BN(3)),
+ })
+ rows.push({
+ chain: shuffled[2],
+ identity: claimInfo.identity,
+ amount: claimInfo.amount
+ .div(new anchor.BN(3))
+ .add(claimInfo.amount.mod(new anchor.BN(3))),
+ })
+ }
+ await addEvmBreakdownsToDatabase(pool, rows)
+}
+
+export async function addSolanaBreakdownsToDatabase(
+ pool: Pool,
+ solanaBreakdowns: SolanaBreakdownRow[]
+) {
+ console.log('INSERTING :', solanaBreakdowns.length, ' SOLANA BREAKDOWNS')
+ const chunks = []
+ while (solanaBreakdowns.length) {
+ chunks.push(
+ solanaBreakdowns.splice(0, CHUNK_SIZE).map((row) => {
+ return {
+ source: row.source,
+ amount: row.amount.toString(),
+ identity: row.identity,
+ }
+ })
+ )
+ }
+
+ const SolanaBreakdowns = sql.define({
+ name: 'solana_breakdowns',
+ columns: ['source', 'identity', 'amount'],
+ })
+
+ for (const chunk of chunks) {
+ const query = SolanaBreakdowns.insert(chunk).toQuery()
+ await pool.query(query)
+ }
+}
+
+export async function addTestSolanaBreakdown(
+ pool: Pool,
+ testSolanaWallets: TestSolanaWallet[]
+): Promise {
+ const claimInfos = testSolanaWallets.map(
+ (testSolanaWallet, index) =>
+ new ClaimInfo(
+ 'solana',
+ testSolanaWallet.address(),
+ new anchor.BN(1000000 * SOLANA_ECOSYSTEM_INDEX + 100000 * index)
+ )
+ )
+ const rows: SolanaBreakdownRow[] = []
+ for (let claimInfo of claimInfos) {
+ rows.push({
+ source: 'nft',
+ identity: claimInfo.identity,
+ amount: claimInfo.amount.div(new anchor.BN(2)),
+ })
+
+ rows.push({
+ source: 'defi',
+ identity: claimInfo.identity,
+ amount: claimInfo.amount
+ .div(new anchor.BN(2))
+ .add(claimInfo.amount.mod(new anchor.BN(2))),
+ })
+ }
+ await addSolanaBreakdownsToDatabase(pool, rows)
+}
diff --git a/frontend/utils/ecosystemEnumToEcosystem.ts b/frontend/utils/ecosystemEnumToEcosystem.ts
index b871b4ec..8f248f23 100644
--- a/frontend/utils/ecosystemEnumToEcosystem.ts
+++ b/frontend/utils/ecosystemEnumToEcosystem.ts
@@ -9,12 +9,10 @@ export function enumToSdkEcosystem(ecosystem: EnumEcosystem): SdkEcosystem {
return 'evm'
case EnumEcosystem.INJECTIVE:
return 'injective'
- case EnumEcosystem.NEUTRON:
+ case EnumEcosystem.TERRA:
return 'cosmwasm'
case EnumEcosystem.OSMOSIS:
return 'cosmwasm'
- case EnumEcosystem.SEI:
- return 'cosmwasm'
case EnumEcosystem.SOLANA:
return 'solana'
case EnumEcosystem.SUI:
diff --git a/frontend/utils/getEcosystemTableLabel.ts b/frontend/utils/getEcosystemTableLabel.ts
index ef39ce2e..0d2eeba9 100644
--- a/frontend/utils/getEcosystemTableLabel.ts
+++ b/frontend/utils/getEcosystemTableLabel.ts
@@ -9,12 +9,10 @@ export function getEcosystemTableLabel(ecosystem: Ecosystem) {
return 'EVM activity'
case Ecosystem.INJECTIVE:
return 'Injective activity'
- case Ecosystem.NEUTRON:
- return 'Neutron activity'
+ case Ecosystem.TERRA:
+ return 'Terra activity'
case Ecosystem.OSMOSIS:
return 'Osmosis activity'
- case Ecosystem.SEI:
- return 'Sei activity'
case Ecosystem.SOLANA:
return 'Solana activity'
case Ecosystem.SUI:
diff --git a/token-dispenser/programs/token-dispenser/src/ecosystems/cosmos.rs b/token-dispenser/programs/token-dispenser/src/ecosystems/cosmos.rs
index 11e3672b..706a4590 100644
--- a/token-dispenser/programs/token-dispenser/src/ecosystems/cosmos.rs
+++ b/token-dispenser/programs/token-dispenser/src/ecosystems/cosmos.rs
@@ -31,7 +31,7 @@ use {
pub const EXPECTED_COSMOS_MESSAGE_TYPE: &str = "sign/MsgSignData";
pub const INJECTIVE_CHAIN_ID: &str = "inj";
-pub const ADMISSIBLE_CHAIN_IDS: [&str; 3] = ["sei", "neutron", "osmo"];
+pub const ADMISSIBLE_CHAIN_IDS: [&str; 2] = ["terra", "osmo"];
/**
* An ADR036 message used in Cosmos. ADR036 is a standard for signing arbitrary data.
diff --git a/token-dispenser/programs/token-dispenser/src/tests/test_cosmos.rs b/token-dispenser/programs/token-dispenser/src/tests/test_cosmos.rs
index f31bc233..ba4da87c 100644
--- a/token-dispenser/programs/token-dispenser/src/tests/test_cosmos.rs
+++ b/token-dispenser/programs/token-dispenser/src/tests/test_cosmos.rs
@@ -92,7 +92,10 @@ pub fn test_authorized_cosmos_chain_ids() {
let secret = libsecp256k1::SecretKey::random(&mut rand::thread_rng());
let public_key = libsecp256k1::PublicKey::from_secret_key(&secret);
assert!(UncompressedSecp256k1Pubkey::from(public_key.serialize())
- .into_bech32("neutron")
+ .into_bech32("terra")
+ .is_ok());
+ assert!(UncompressedSecp256k1Pubkey::from(public_key.serialize())
+ .into_bech32("osmo")
.is_ok());
assert_eq!(
UncompressedSecp256k1Pubkey::from(public_key.serialize())