UNPKG

@surec/oracle

Version:

Typescript SDK for the Sure Oracle to be used to bring off-chain data on-chain

126 lines 5.02 kB
import { TransactionInstruction, } from '@solana/web3.js'; import * as anchor from '@project-serum/anchor'; import { SHAKE } from 'sha3'; import * as spl from '@solana/spl-token'; export const validateKeys = (keys) => { const undefinedErrors = keys .filter((k) => k.v === undefined) .map((k) => `${k.n} is undefined.`) .join(', '); if (undefinedErrors.length > 0) { throw new Error(undefinedErrors); } }; export const getProposalStatus = (proposal) => { const currentTime = new anchor.BN(Math.floor(Date.now() / 1000)); const hasReachedQuorum = proposal.votes >= proposal.requiredVotes; const isScaleParameterCalculated = proposal.scaleParameterCalculated; const isLocked = proposal.locked; if (isBlindVoteOngoing(proposal, currentTime) && !hasReachedQuorum) { return 'Voting'; } else if (isBlindVoteFinished(proposal, currentTime) && hasReachedQuorum) { return 'Reveal'; } else if (isRevealVoteFinished(proposal, currentTime) && hasReachedQuorum) { return 'Creating reward distribution'; } else if (isScaleParameterCalculated) { return 'Calculate Reward'; } else if (isLocked) { return 'Reward Payout'; } else { return 'Failed'; } }; export const proposalFailReason = (proposal) => { if (getProposalStatus(proposal) == 'Failed') { const hasReachedQuorum = proposal.votes >= proposal.requiredVotes; if (!hasReachedQuorum) { return 'NotEnoughVotes'; } } return 'Unknown'; }; export const getVoteStatus = (proposal) => { const propsalStatus = getProposalStatus(proposal); if (propsalStatus == 'Voting') { return 'Voting'; } else if (propsalStatus == 'Reveal') { return 'Reveal vote'; } else if (propsalStatus == 'Creating reward distribution' || propsalStatus == 'Calculate Reward') { return 'Calculate Reward'; } else if (propsalStatus == 'Reward Payout') { return 'Collect Reward'; } else { return 'Failed'; } }; const isBlindVoteOngoing = (proposal, currentTime) => { return (currentTime >= proposal.voteStartAt && currentTime < proposal.voteEndAt); }; const isBlindVoteFinished = (proposal, currentTime) => { return (currentTime >= proposal.voteEndAt && currentTime < proposal.voteEndRevealAt); }; const isRevealVoteFinished = (proposal, currentTime) => { return currentTime >= proposal.voteEndRevealAt; }; export const createProposalHash = ({ name }) => { const hash = new SHAKE(128); hash.update(name); return hash.digest(); }; export const getOrCreateAssociatedTokenAccountIx = async ({ connection, payer, mint, owner, allowOwnerOffCurve = false, commitment, programId = spl.TOKEN_PROGRAM_ID, associatedTokenProgramId = spl.ASSOCIATED_TOKEN_PROGRAM_ID, }) => { const associatedToken = await spl.getAssociatedTokenAddress(mint, owner, allowOwnerOffCurve, programId, associatedTokenProgramId); // This is the optimal logic, considering TX fee, client-side computation, RPC roundtrips and guaranteed idempotent. // Sadly we can't do this atomically. let account; try { account = await spl.getAccount(connection, associatedToken, commitment, programId); return { instruction: null, address: associatedToken, }; } catch (error) { // TokenAccountNotFoundError can be possible if the associated address has already received some lamports, // becoming a system account. Assuming program derived addressing is safe, this is the only case for the // TokenInvalidAccountOwnerError in this code path. if (error instanceof spl.TokenAccountNotFoundError || error instanceof spl.TokenInvalidOwnerError) { // As this isn't atomic, it's possible others can create associated accounts meanwhile. try { const transaction = new TransactionInstruction(spl.createAssociatedTokenAccountInstruction(payer.publicKey, associatedToken, owner, mint, programId, associatedTokenProgramId)); return { instruction: transaction, address: associatedToken, }; } catch (error) { // Ignore all errors; for now there is no API-compatible way to selectively ignore the expected // instruction error if the associated account exists already. } // Now this should always succeed account = await spl.getAccount(connection, associatedToken, commitment, programId); } else { throw error; } } if (!account.mint.equals(mint)) throw new spl.TokenInvalidMintError(); if (!account.owner.equals(owner)) throw new spl.TokenInvalidOwnerError(); return { address: associatedToken, instruction: null, }; }; //# sourceMappingURL=utils.js.map