diff --git a/frontend/claim_sdk/claim.ts b/frontend/claim_sdk/claim.ts index 7c20aa1f..0eaa0fd1 100644 --- a/frontend/claim_sdk/claim.ts +++ b/frontend/claim_sdk/claim.ts @@ -12,7 +12,8 @@ export type Ecosystem = | 'evm' | 'sui' | 'aptos' - | 'cosmwasm' + | 'terra' + | 'osmosis' | 'injective' export const Ecosystems: Ecosystem[] = [ 'discord', @@ -20,7 +21,8 @@ export const Ecosystems: Ecosystem[] = [ 'evm', 'sui', 'aptos', - 'cosmwasm', + 'terra', + 'osmosis', 'injective', ] @@ -53,7 +55,8 @@ export class ClaimInfo { } break } - case 'cosmwasm': { + case 'osmosis': + case 'terra': { identityStruct = { cosmwasm: { address: this.identity }, } diff --git a/frontend/claim_sdk/merkleTree.ts b/frontend/claim_sdk/merkleTree.ts index b2133e84..fa47044b 100644 --- a/frontend/claim_sdk/merkleTree.ts +++ b/frontend/claim_sdk/merkleTree.ts @@ -4,6 +4,7 @@ const LEAF_PREFIX = Buffer.from('00', 'hex') const NODE_PREFIX = Buffer.from('01', 'hex') const NULL_PREFIX = Buffer.from('02', 'hex') +// The size of the hash output in bytes export const HASH_SIZE = 20 export class MerkleTree { diff --git a/frontend/claim_sdk/solana.ts b/frontend/claim_sdk/solana.ts index 5ee63d04..5401c7e2 100644 --- a/frontend/claim_sdk/solana.ts +++ b/frontend/claim_sdk/solana.ts @@ -376,7 +376,8 @@ export class TokenDispenserProvider { }, } } - case 'cosmwasm': { + case 'osmosis': + case 'terra': { return { cosmwasm: { pubkey: Array.from(signedMessage.publicKey), @@ -426,7 +427,8 @@ export class TokenDispenserProvider { recoveryId: signedMessage.recoveryId!, }) } - case 'cosmwasm': { + case 'osmosis': + case 'terra': { return undefined } case 'discord': diff --git a/frontend/claim_sdk/testWallets.ts b/frontend/claim_sdk/testWallets.ts index 34a1475c..cf2ebbd1 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' @@ -79,7 +80,8 @@ export async function loadTestWallets(): Promise< evm: [], sui: [], aptos: [], - cosmwasm: [], + terra: [], + osmosis: [], injective: [], } result['discord'] = [ @@ -89,10 +91,12 @@ export async function loadTestWallets(): Promise< result['evm'] = [TestEvmWallet.fromKeyfile(evmPrivateKeyPath)] 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'), + // prettier-ignore + result['osmosis'] = [await TestCosmWasmWallet.fromKeyFile(cosmosPrivateKeyPath, 'osmo')] + // prettier-ignore + result['terra'] = [await TestCosmWasmWallet.fromKeyFile(cosmosPrivateKeyPath, 'terra', [ + stringToPath("m/44'/330'/0'/0/0"), + ]), ] result['injective'] = [TestEvmWallet.fromKeyfile(cosmosPrivateKeyPath, true)] @@ -150,14 +154,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/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/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/api.ts b/frontend/integration/api.ts index 8764d6a6..d4e5e093 100644 --- a/frontend/integration/api.ts +++ b/frontend/integration/api.ts @@ -9,7 +9,6 @@ import { } from '@solana/web3.js' import { NextApiRequest, NextApiResponse } from 'next' import { - getAmountAndProofRoute, getFundTransactionRoute, handleAmountAndProofResponse, handleFundTransaction, @@ -38,6 +37,10 @@ const WHITELISTED_PROGRAMS: PublicKey[] = [ ComputeBudgetProgram.programId, ] +function getAmountAndProofRoute(..._: any[]): string { + return '' +} + function lowerCapIfEvm(identity: string, ecosystem: string): string { if (ecosystem === 'evm') { return identity.toLowerCase() diff --git a/frontend/integration/integrationTest.test.ts b/frontend/integration/integrationTest.test.ts index ea2e8db5..098d7635 100644 --- a/frontend/integration/integrationTest.test.ts +++ b/frontend/integration/integrationTest.test.ts @@ -242,11 +242,11 @@ describe('integration test', () => { it('submits a cosmwasm claim', async () => { const { claimInfo, proofOfInclusion } = (await mockFetchAmountAndProof( - 'cosmwasm', - testWallets.cosmwasm[0].address() + 'terra', + testWallets.terra[0].address() ))! - const signedMessage = await testWallets.cosmwasm[0].signMessage( + const signedMessage = await testWallets.terra[0].signMessage( tokenDispenserProvider.generateAuthorizationPayload() ) @@ -283,7 +283,7 @@ describe('integration test', () => { const cosmClaimEvent = txnEvents[0].event! expect(cosmClaimEvent.claimant.equals(wallet.publicKey)).toBeTruthy() expect(cosmClaimEvent.claimInfo.identity).toEqual({ - cosmwasm: { address: testWallets.cosmwasm[0].address() }, + cosmwasm: { address: testWallets.terra[0].address() }, }) expect( new anchor.BN(cosmClaimEvent.claimInfo.amount.toString()).eq( @@ -299,15 +299,12 @@ describe('integration test', () => { }, 40000) it('submits multiple claims at once', async () => { - const wallets: TestWallet[] = [ - testWallets.cosmwasm[1], - testWallets.cosmwasm[2], - ] + const wallets: TestWallet[] = [testWallets.terra[1], testWallets.terra[2]] const claims = await Promise.all( wallets.map(async (wallet) => { const { claimInfo, proofOfInclusion } = - (await mockFetchAmountAndProof('cosmwasm', wallet.address()))! + (await mockFetchAmountAndProof('terra', wallet.address()))! return { claimInfo, proofOfInclusion, 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 788fb89a..0c127af2 100644 --- a/frontend/pages/_app.tsx +++ b/frontend/pages/_app.tsx @@ -11,7 +11,6 @@ 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' @@ -30,7 +29,7 @@ function useRedirect(isVersionChecked: boolean) { const router = useRouter() - // temp disable to get the nav working + // temp disable to get the nav working - TODO Review this before live // We will only redirect on the first load // useLayoutEffect(() => { @@ -71,7 +70,9 @@ const App: FC = ({ Component, pageProps }: AppProps) => { setIsVersionChecked(true) }, [router]) - useRedirect(isVersionChecked) + // TODO Review this, we should check if the user + // loads the page, we should redirect to welcome pages again + // useRedirect(isVersionChecked) return ( <> @@ -81,31 +82,29 @@ const App: FC = ({ Component, pageProps }: AppProps) => { - - {/* 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/setup.sh b/frontend/scripts/setup.sh index 2546f249..33900137 100755 --- a/frontend/scripts/setup.sh +++ b/frontend/scripts/setup.sh @@ -13,7 +13,7 @@ TOKEN_DISPENSER_DIR="$DIR/../../token-dispenser"; usage() { cat < {

I am active on…

{Object.values(Ecosystem).map((ecosystem) => { - if (ecosystem === Ecosystem.DISCORD) return <> - else + if (ecosystem === Ecosystem.DISCORD) { + return + } else { return ( ) + } })}

I am an active member of…

diff --git a/frontend/sections/WalletsEligibility.tsx b/frontend/sections/WalletsEligibility.tsx index aed9ea21..bf1cb6c2 100644 --- a/frontend/sections/WalletsEligibility.tsx +++ b/frontend/sections/WalletsEligibility.tsx @@ -107,6 +107,7 @@ const Eligibility = ({ type TableRowProps = { ecosystem: Ecosystem } + function TableRow({ ecosystem }: TableRowProps) { const { activity } = useActivity() const getEcosystemIdentity = useGetEcosystemIdentity() @@ -181,6 +182,7 @@ function TableRow({ ecosystem }: TableRowProps) { eligibility?.isClaimAlreadySubmitted, identity, isActive, + ecosystem, ]) return ( diff --git a/frontend/utils/api.ts b/frontend/utils/api.ts index ff258931..55bdffb8 100644 --- a/frontend/utils/api.ts +++ b/frontend/utils/api.ts @@ -3,8 +3,13 @@ import { ClaimInfo, Ecosystem } from '../claim_sdk/claim' import { HASH_SIZE } from '../claim_sdk/merkleTree' import { PublicKey, VersionedTransaction } from '@solana/web3.js' import { SignedMessage } from '../claim_sdk/ecosystems/signatures' +import { ECOSYSTEM_IDS } from './constants' + +const MERKLE_PROOFS = process.env.NEXT_PUBLIC_MERKLE_PROOFS function parseProof(proof: string) { + // TODO remove it, we should not have empty proofs and will fail if tahat happens + if (!proof || proof === '') return [] const buffer = Buffer.from(proof, 'hex') const chunks = [] @@ -19,47 +24,73 @@ function parseProof(proof: string) { return chunks } -export function getAmountAndProofRoute( +const getAmountAndProofRoute = ( ecosystem: Ecosystem, identity: string -): string { - return `/api/grant/v1/amount_and_proof?ecosystem=${ecosystem}&identity=${identity}` +): string[] => { + if (ecosystem === 'evm') { + return [ + `${MERKLE_PROOFS}/${identity.toLowerCase()}_${ + ECOSYSTEM_IDS[ecosystem] + }.json`, + `${MERKLE_PROOFS}/${identity.toUpperCase()}_${ + ECOSYSTEM_IDS[ecosystem] + }.json`, + `${MERKLE_PROOFS}/${identity}_${ECOSYSTEM_IDS[ecosystem]}.json`, + ] + } else { + return [`${MERKLE_PROOFS}/${identity}_${ECOSYSTEM_IDS[ecosystem]}.json`] + } } +// TODO refactor/remove export function handleAmountAndProofResponse( ecosystem: Ecosystem, identity: string, status: number, - data: any + { address, amount, hashes }: any = {} ): { claimInfo: ClaimInfo; proofOfInclusion: Uint8Array[] } | undefined { if (status == 404) return undefined if (status == 200) { - return { - claimInfo: new ClaimInfo(ecosystem, identity, new BN(data.amount)), - proofOfInclusion: parseProof(data.proof), + if (identity === address) { + return { + claimInfo: new ClaimInfo(ecosystem, identity, new BN(amount)), + proofOfInclusion: parseProof(hashes), + } } } } // If the given identity is not eligible the value will be undefined // Else the value contains the eligibility information -export type Eligibility = - | { claimInfo: ClaimInfo; proofOfInclusion: Uint8Array[] } - | undefined +export type Eligibility = { + claimInfo: ClaimInfo + proofOfInclusion: Uint8Array[] +} + export async function fetchAmountAndProof( ecosystem: Ecosystem, identity: string -): Promise { - const response = await fetch(getAmountAndProofRoute(ecosystem, identity)) - return handleAmountAndProofResponse( - ecosystem, - identity, - response.status, - await response.json() - ) +): Promise { + const files = getAmountAndProofRoute(ecosystem, identity) + // Iterate over each posible file name and return the first valid response + // The best case will be to have only one file per identity + for (const file of files) { + const response = await fetch(file) + if (response.headers.get('content-type') === 'application/json') { + const data = await response.json() + if (response.status === 200 && data.address === identity) { + return { + claimInfo: new ClaimInfo(ecosystem, identity, new BN(data.amount)), + proofOfInclusion: parseProof(data.hashes), + } + } + } + } } export function getDiscordSignedMessageRoute(claimant: PublicKey) { + // TODO update it with lambda route return `/api/grant/v1/discord_signed_message?publicKey=${claimant.toBase58()}` } @@ -88,6 +119,7 @@ export async function fetchDiscordSignedMessage( } export function getFundTransactionRoute(): string { + // TODO update it with lambda route return `/api/grant/v1/fund_transaction` } 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..446b8898 100644 --- a/frontend/utils/constants.ts +++ b/frontend/utils/constants.ts @@ -1,6 +1,19 @@ +import { Ecosystem } from 'claim_sdk/claim' + // TODO remove it export type SOLANA_SOURCES = 'nft' | 'defi' +export const ECOSYSTEM_IDS: Record = { + solana: 1, + evm: 2, + terra: 3, + injective: 19, + osmosis: 20, + sui: 21, + aptos: 22, + discord: 14443, +} as const + export const EVM_CHAINS = [ 'optimism-mainnet', 'arbitrum-mainnet', @@ -24,4 +37,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..d50e8c99 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: - return 'cosmwasm' + case EnumEcosystem.TERRA: + return 'terra' case EnumEcosystem.OSMOSIS: - return 'cosmwasm' - case EnumEcosystem.SEI: - return 'cosmwasm' + return 'osmosis' 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())